From 7f55c4e5135d7ae5b68117b142e8cffb0a58599a Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Fri, 15 Jul 2022 11:02:38 +0200 Subject: [PATCH 001/253] graphql,store: child filters on interface-type fields --- graphql/src/schema/api.rs | 30 ++++-------- graphql/src/store/query.rs | 45 ++++++++++++------ graphql/tests/query.rs | 95 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 34 deletions(-) diff --git a/graphql/src/schema/api.rs b/graphql/src/schema/api.rs index b1500b771f8..c7bff2dbcc2 100644 --- a/graphql/src/schema/api.rs +++ b/graphql/src/schema/api.rs @@ -228,7 +228,7 @@ fn field_filter_input_values( .get_named_type(name) .ok_or_else(|| APISchemaError::TypeNotFound(name.clone()))?; Ok(match named_type { - TypeDefinition::Object(_) => { + TypeDefinition::Object(_) | TypeDefinition::Interface(_) => { let mut input_values = match ast::get_derived_from_directive(field) { // Only add `where` filter fields for object and interface fields // if they are not @derivedFrom @@ -246,23 +246,6 @@ fn field_filter_input_values( extend_with_child_filter_input_value(field, name, &mut input_values); input_values } - TypeDefinition::Interface(_) => { - // Only add `where` filter fields for object and interface fields - // if they are not @derivedFrom - if ast::get_derived_from_directive(field).is_some() { - vec![] - } else { - // We allow filtering with `where: { other: "some-id" }` and - // `where: { others: ["some-id", "other-id"] }`. In both cases, - // we allow ID strings as the values to be passed to these - // filters. - field_scalar_filter_input_values( - schema, - field, - &ScalarType::new(String::from("String")), - ) - } - } TypeDefinition::Scalar(ref t) => field_scalar_filter_input_values(schema, field, t), TypeDefinition::Enum(ref t) => field_enum_filter_input_values(schema, field, t), _ => vec![], @@ -381,11 +364,14 @@ fn field_list_filter_input_values( ) } } - TypeDefinition::Interface(_) => { + TypeDefinition::Interface(parent) => { if ast::get_derived_from_directive(field).is_some() { - (None, None) + (None, Some(parent.name.clone())) } else { - (Some(Type::NamedType("String".into())), None) + ( + Some(Type::NamedType("String".into())), + Some(parent.name.clone()), + ) } } TypeDefinition::Scalar(ref t) => (Some(Type::NamedType(t.name.to_owned())), None), @@ -1174,6 +1160,7 @@ mod tests { "name_ends_with_nocase", "name_not_ends_with", "name_not_ends_with_nocase", + "pets_", "favoritePet", "favoritePet_not", "favoritePet_gt", @@ -1194,6 +1181,7 @@ mod tests { "favoritePet_ends_with_nocase", "favoritePet_not_ends_with", "favoritePet_not_ends_with_nocase", + "favoritePet_", "_change_block" ] .iter() diff --git a/graphql/src/store/query.rs b/graphql/src/store/query.rs index e9b38e3017a..683326d40ae 100644 --- a/graphql/src/store/query.rs +++ b/graphql/src/store/query.rs @@ -261,21 +261,40 @@ fn build_child_filter_from_object( let child_entity = schema .object_or_interface(type_name) .ok_or(QueryExecutionError::InvalidFilterError)?; - let filter = build_filter_from_object(child_entity, object, schema)?; + let filter = Box::new(build_filter_from_object(child_entity, object, schema)?); let derived = field.is_derived(); + let attr = match derived { + true => sast::get_derived_from_field(child_entity, field) + .ok_or(QueryExecutionError::InvalidFilterError)? + .name + .to_string(), + false => field_name, + }; - Ok(EntityFilter::Child(Child { - attr: match derived { - true => sast::get_derived_from_field(child_entity, field) - .ok_or(QueryExecutionError::InvalidFilterError)? - .name - .to_string(), - false => field_name, - }, - entity_type: EntityType::new(type_name.to_string()), - filter: Box::new(filter), - derived, - })) + if child_entity.is_interface() { + Ok(EntityFilter::Or( + child_entity + .object_types(schema.schema()) + .expect("Interface is not implemented by any types") + .iter() + .map(|object_type| { + EntityFilter::Child(Child { + attr: attr.clone(), + entity_type: EntityType::new(object_type.name.to_string()), + filter: filter.clone(), + derived, + }) + }) + .collect(), + )) + } else { + Ok(EntityFilter::Child(Child { + attr, + entity_type: EntityType::new(type_name.to_string()), + filter, + derived, + })) + } } /// Parses a list of GraphQL values into a vector of entity field values. diff --git a/graphql/tests/query.rs b/graphql/tests/query.rs index 854f8782dd6..9f0d46bccd4 100644 --- a/graphql/tests/query.rs +++ b/graphql/tests/query.rs @@ -42,6 +42,8 @@ use test_store::{ const NETWORK_NAME: &str = "fake_network"; const SONGS_STRING: [&str; 5] = ["s0", "s1", "s2", "s3", "s4"]; const SONGS_BYTES: [&str; 5] = ["0xf0", "0xf1", "0xf2", "0xf3", "0xf4"]; +const REVIEWS_STRING: [&str; 5] = ["r0", "r1", "r2", "r3", "r4"]; +const REVIEWS_BYTES: [&str; 5] = ["0xf0", "0xf1", "0xf2", "0xf3", "0xf4"]; #[derive(Clone, Copy, Debug)] enum IdType { @@ -58,6 +60,13 @@ impl IdType { } } + fn reviews(&self) -> &[&str] { + match self { + IdType::String => REVIEWS_STRING.as_slice(), + IdType::Bytes => REVIEWS_BYTES.as_slice(), + } + } + fn as_str(&self) -> &str { match self { IdType::String => "String", @@ -155,6 +164,7 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { id: ID! name: String! members: [Musician!]! @derivedFrom(field: \"bands\") + reviews: [BandReview] @derivedFrom(field: \"band\") originalSongs: [Song!]! } @@ -164,6 +174,7 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { writtenBy: Musician! publisher: Publisher! band: Band @derivedFrom(field: \"originalSongs\") + reviews: [SongReview] @derivedFrom(field: \"song\") } type SongStat @entity { @@ -175,6 +186,34 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { type Publisher { id: Bytes! } + + interface Review { + id: ID! + body: String! + author: User! + } + + type SongReview implements Review @entity { + id: ID! + body: String! + song: Song + author: User! + } + + type BandReview implements Review @entity { + id: ID! + body: String! + band: Band + author: User! + } + + type User @entity { + id: ID! + name: String! + bandReviews: [BandReview] @derivedFrom(field: \"author\") + songReviews: [SongReview] @derivedFrom(field: \"author\") + reviews: [Review] @derivedFrom(field: \"author\") + } "; Schema::parse(&SCHEMA.replace("@ID@", id_type.as_str()), id).expect("Test schema invalid") @@ -200,6 +239,7 @@ async fn insert_test_entities( .unwrap(); let s = id_type.songs(); + let r = id_type.reviews(); let entities0 = vec![ entity! { __typename: "Musician", id: "m1", name: "John", mainBand: "b1", bands: vec!["b1", "b2"] }, entity! { __typename: "Musician", id: "m2", name: "Lisa", mainBand: "b1", bands: vec!["b1"] }, @@ -212,6 +252,12 @@ async fn insert_test_entities( entity! { __typename: "Song", id: s[4], title: "Folk Tune", publisher: "0xb1", writtenBy: "m3" }, entity! { __typename: "SongStat", id: s[1], played: 10 }, entity! { __typename: "SongStat", id: s[2], played: 15 }, + entity! { __typename: "BandReview", id: r[1], body: "Bad musicians", band: "b1", author: "u1" }, + entity! { __typename: "BandReview", id: r[2], body: "Good amateurs", band: "b2", author: "u2" }, + entity! { __typename: "SongReview", id: r[3], body: "Bad", song: s[2], author: "u1" }, + entity! { __typename: "SongReview", id: r[4], body: "Good", song: s[3], author: "u2" }, + entity! { __typename: "User", id: "u1", name: "Baden" }, + entity! { __typename: "User", id: "u2", name: "Goodwill"}, ]; let entities1 = vec![ @@ -661,6 +707,55 @@ fn can_query_with_child_filter_on_derived_named_type_field() { }) } +#[test] +fn can_query_an_interface_with_child_filter_on_named_type_field() { + const QUERY: &str = " + query { + reviews(first: 100, orderBy: id, where: { author_: { name_starts_with: \"Good\" } }) { + body + author { + name + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + reviews: vec![ + object! { body: "Good amateurs", author: object! { name: "Goodwill" } }, + object! { body: "Good", author: object! { name: "Goodwill" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_with_child_filter_on_interface_field() { + const QUERY: &str = " + query { + users(first: 100, orderBy: id, where: { reviews_: { body_starts_with: \"Good\" } }) { + name + reviews { + body + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + users: vec![ + object! { name: "Goodwill", reviews: vec![ object! { body: "Good amateurs" }, object! { body: "Good" } ] }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + #[test] fn root_fragments_are_expanded() { const QUERY: &str = r#" From 2231e7d136c0f88cef94cdf5b8e0d177e94ff38c Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Sep 2022 16:47:30 -0700 Subject: [PATCH 002/253] store: Handle is type 'Bytes' correctly for children type c We did not account for parent ids being `bytea` when generating queries for children type c which only caused trouble when such entities were used in interfaces. --- store/postgres/src/relational_queries.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/store/postgres/src/relational_queries.rs b/store/postgres/src/relational_queries.rs index e62de38f14c..c3b435e5eb3 100644 --- a/store/postgres/src/relational_queries.rs +++ b/store/postgres/src/relational_queries.rs @@ -1906,6 +1906,7 @@ impl ParentIds { #[derive(Debug, Clone)] enum TableLink<'a> { Direct(&'a Column, ChildMultiplicity), + /// The `Table` is the parent table Parent(&'a Table, ParentIds), } @@ -2190,6 +2191,7 @@ impl<'a> FilterWindow<'a> { fn children_type_c( &self, + parent_primary_key: &Column, child_ids: &[Vec>], limit: ParentLimit<'_>, block: BlockNumber, @@ -2209,7 +2211,7 @@ impl<'a> FilterWindow<'a> { out.push_sql("\n/* children_type_c */ from "); out.push_sql("rows from (unnest("); - out.push_bind_param::, _>(&self.ids)?; + parent_primary_key.bind_ids(&self.ids, out)?; out.push_sql("), reduce_dim("); self.table.primary_key().push_matrix(child_ids, out)?; out.push_sql(")) as p(id, child_ids)"); @@ -2292,9 +2294,13 @@ impl<'a> FilterWindow<'a> { } } } - TableLink::Parent(_, ParentIds::List(child_ids)) => { - self.children_type_c(child_ids, limit, block, &mut out) - } + TableLink::Parent(parent_table, ParentIds::List(child_ids)) => self.children_type_c( + parent_table.primary_key(), + child_ids, + limit, + block, + &mut out, + ), TableLink::Parent(_, ParentIds::Scalar(child_ids)) => { self.child_type_d(child_ids, limit, block, &mut out) } From 93395eb31eb99fc9d4aa4957c1bb59dff441c4b3 Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Thu, 28 Jul 2022 15:16:34 +0200 Subject: [PATCH 003/253] graphql: Test and support more kinds of child/parent relations See https://github.com/graphprotocol/graph-node/pull/3677#issuecomment-1213594946 for a list of possible parent/child types and how they are related --- graphql/src/schema/api.rs | 20 +-- graphql/src/store/query.rs | 39 +++++- graphql/tests/query.rs | 273 +++++++++++++++++++++++++++++++++---- 3 files changed, 291 insertions(+), 41 deletions(-) diff --git a/graphql/src/schema/api.rs b/graphql/src/schema/api.rs index c7bff2dbcc2..03d061c4c3e 100644 --- a/graphql/src/schema/api.rs +++ b/graphql/src/schema/api.rs @@ -354,24 +354,12 @@ fn field_list_filter_input_values( // derived, we allow ID strings to be passed on. // Adds child filter only to object types. let (input_field_type, parent_type_name) = match typedef { - TypeDefinition::Object(parent) => { + TypeDefinition::Object(ObjectType { name, .. }) + | TypeDefinition::Interface(InterfaceType { name, .. }) => { if ast::get_derived_from_directive(field).is_some() { - (None, Some(parent.name.clone())) + (None, Some(name.clone())) } else { - ( - Some(Type::NamedType("String".into())), - Some(parent.name.clone()), - ) - } - } - TypeDefinition::Interface(parent) => { - if ast::get_derived_from_directive(field).is_some() { - (None, Some(parent.name.clone())) - } else { - ( - Some(Type::NamedType("String".into())), - Some(parent.name.clone()), - ) + (Some(Type::NamedType("String".into())), Some(name.clone())) } } TypeDefinition::Scalar(ref t) => (Some(Type::NamedType(t.name.to_owned())), None), diff --git a/graphql/src/store/query.rs b/graphql/src/store/query.rs index 683326d40ae..c31b00b69cc 100644 --- a/graphql/src/store/query.rs +++ b/graphql/src/store/query.rs @@ -268,14 +268,16 @@ fn build_child_filter_from_object( .ok_or(QueryExecutionError::InvalidFilterError)? .name .to_string(), - false => field_name, + false => field_name.clone(), }; if child_entity.is_interface() { Ok(EntityFilter::Or( child_entity .object_types(schema.schema()) - .expect("Interface is not implemented by any types") + .ok_or(QueryExecutionError::AbstractTypeError( + "Interface is not implemented by any types".to_string(), + ))? .iter() .map(|object_type| { EntityFilter::Child(Child { @@ -287,6 +289,39 @@ fn build_child_filter_from_object( }) .collect(), )) + } else if entity.is_interface() { + Ok(EntityFilter::Or( + entity + .object_types(schema.schema()) + .ok_or(QueryExecutionError::AbstractTypeError( + "Interface is not implemented by any types".to_string(), + ))? + .iter() + .map(|object_type| { + let field = object_type + .fields + .iter() + .find(|f| f.name == field_name.clone()) + .ok_or(QueryExecutionError::InvalidFilterError)?; + let derived = field.is_derived(); + + let attr = match derived { + true => sast::get_derived_from_field(child_entity, field) + .ok_or(QueryExecutionError::InvalidFilterError)? + .name + .to_string(), + false => field_name.clone(), + }; + + Ok(EntityFilter::Child(Child { + attr: attr.clone(), + entity_type: EntityType::new(child_entity.name().to_string()), + filter: filter.clone(), + derived, + })) + }) + .collect::, QueryExecutionError>>()?, + )) } else { Ok(EntityFilter::Child(Child { attr, diff --git a/graphql/tests/query.rs b/graphql/tests/query.rs index 9f0d46bccd4..0c0b01da4ac 100644 --- a/graphql/tests/query.rs +++ b/graphql/tests/query.rs @@ -42,8 +42,8 @@ use test_store::{ const NETWORK_NAME: &str = "fake_network"; const SONGS_STRING: [&str; 5] = ["s0", "s1", "s2", "s3", "s4"]; const SONGS_BYTES: [&str; 5] = ["0xf0", "0xf1", "0xf2", "0xf3", "0xf4"]; -const REVIEWS_STRING: [&str; 5] = ["r0", "r1", "r2", "r3", "r4"]; -const REVIEWS_BYTES: [&str; 5] = ["0xf0", "0xf1", "0xf2", "0xf3", "0xf4"]; +const MEDIA_STRING: [&str; 7] = ["md0", "md1", "md2", "md3", "md4", "md5", "md6"]; +const MEDIA_BYTES: [&str; 7] = ["0xf0", "0xf1", "0xf2", "0xf3", "0xf4", "0xf5", "0xf6"]; #[derive(Clone, Copy, Debug)] enum IdType { @@ -60,10 +60,10 @@ impl IdType { } } - fn reviews(&self) -> &[&str] { + fn medias(&self) -> &[&str] { match self { - IdType::String => REVIEWS_STRING.as_slice(), - IdType::Bytes => REVIEWS_BYTES.as_slice(), + IdType::String => MEDIA_STRING.as_slice(), + IdType::Bytes => MEDIA_BYTES.as_slice(), } } @@ -157,14 +157,14 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { name: String! mainBand: Band bands: [Band!]! - writtenSongs: [Song]! @derivedFrom(field: \"writtenBy\") + writtenSongs: [Song!]! @derivedFrom(field: \"writtenBy\") } type Band @entity { id: ID! name: String! members: [Musician!]! @derivedFrom(field: \"bands\") - reviews: [BandReview] @derivedFrom(field: \"band\") + reviews: [BandReview!]! @derivedFrom(field: \"band\") originalSongs: [Song!]! } @@ -174,7 +174,9 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { writtenBy: Musician! publisher: Publisher! band: Band @derivedFrom(field: \"originalSongs\") - reviews: [SongReview] @derivedFrom(field: \"song\") + reviews: [SongReview!]! @derivedFrom(field: \"song\") + media: [Media!]! + release: Release! @derivedFrom(field: \"songs\") } type SongStat @entity { @@ -210,9 +212,50 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { type User @entity { id: ID! name: String! - bandReviews: [BandReview] @derivedFrom(field: \"author\") - songReviews: [SongReview] @derivedFrom(field: \"author\") - reviews: [Review] @derivedFrom(field: \"author\") + bandReviews: [BandReview!]! @derivedFrom(field: \"author\") + songReviews: [SongReview!]! @derivedFrom(field: \"author\") + reviews: [Review!]! @derivedFrom(field: \"author\") + latestSongReview: SongReview! + latestBandReview: BandReview! + latestReview: Review! + } + + interface Media { + id: ID! + title: String! + song: Song! + } + + type Photo implements Media @entity { + id: ID! + title: String! + song: Song! @derivedFrom(field: \"media\") + } + + type Video implements Media @entity { + id: ID! + title: String! + song: Song! @derivedFrom(field: \"media\") + } + + interface Release { + id: ID! + title: String! + songs: [Song!]! + } + + type Single implements Release @entity { + id: ID! + title: String! + # It could be a single song + # but let's say a Single represents one song + bonus tracks + songs: [Song!]! + } + + type Album implements Release @entity { + id: ID! + title: String! + songs: [Song!]! } "; @@ -239,25 +282,34 @@ async fn insert_test_entities( .unwrap(); let s = id_type.songs(); - let r = id_type.reviews(); + let md = id_type.medias(); let entities0 = vec![ entity! { __typename: "Musician", id: "m1", name: "John", mainBand: "b1", bands: vec!["b1", "b2"] }, entity! { __typename: "Musician", id: "m2", name: "Lisa", mainBand: "b1", bands: vec!["b1"] }, entity! { __typename: "Publisher", id: "0xb1" }, entity! { __typename: "Band", id: "b1", name: "The Musicians", originalSongs: vec![s[1], s[2]] }, entity! { __typename: "Band", id: "b2", name: "The Amateurs", originalSongs: vec![s[1], s[3], s[4]] }, - entity! { __typename: "Song", id: s[1], title: "Cheesy Tune", publisher: "0xb1", writtenBy: "m1" }, - entity! { __typename: "Song", id: s[2], title: "Rock Tune", publisher: "0xb1", writtenBy: "m2" }, - entity! { __typename: "Song", id: s[3], title: "Pop Tune", publisher: "0xb1", writtenBy: "m1" }, - entity! { __typename: "Song", id: s[4], title: "Folk Tune", publisher: "0xb1", writtenBy: "m3" }, + entity! { __typename: "Song", id: s[1], title: "Cheesy Tune", publisher: "0xb1", writtenBy: "m1", media: vec![md[1], md[2]] }, + entity! { __typename: "Song", id: s[2], title: "Rock Tune", publisher: "0xb1", writtenBy: "m2", media: vec![md[3], md[4]] }, + entity! { __typename: "Song", id: s[3], title: "Pop Tune", publisher: "0xb1", writtenBy: "m1", media: vec![md[5]] }, + entity! { __typename: "Song", id: s[4], title: "Folk Tune", publisher: "0xb1", writtenBy: "m3", media: vec![md[6]] }, entity! { __typename: "SongStat", id: s[1], played: 10 }, entity! { __typename: "SongStat", id: s[2], played: 15 }, - entity! { __typename: "BandReview", id: r[1], body: "Bad musicians", band: "b1", author: "u1" }, - entity! { __typename: "BandReview", id: r[2], body: "Good amateurs", band: "b2", author: "u2" }, - entity! { __typename: "SongReview", id: r[3], body: "Bad", song: s[2], author: "u1" }, - entity! { __typename: "SongReview", id: r[4], body: "Good", song: s[3], author: "u2" }, - entity! { __typename: "User", id: "u1", name: "Baden" }, - entity! { __typename: "User", id: "u2", name: "Goodwill"}, + entity! { __typename: "BandReview", id: "r1", body: "Bad musicians", band: "b1", author: "u1" }, + entity! { __typename: "BandReview", id: "r2", body: "Good amateurs", band: "b2", author: "u2" }, + entity! { __typename: "SongReview", id: "r3", body: "Bad", song: s[2], author: "u1" }, + entity! { __typename: "SongReview", id: "r4", body: "Good", song: s[3], author: "u2" }, + entity! { __typename: "User", id: "u1", name: "Baden", latestSongReview: "r3", latestBandReview: "r1", latestReview: "r1" }, + entity! { __typename: "User", id: "u2", name: "Goodwill", latestSongReview: "r4", latestBandReview: "r2", latestReview: "r2" }, + entity! { __typename: "Photo", id: md[1], title: "Cheesy Tune Single Cover" }, + entity! { __typename: "Video", id: md[2], title: "Cheesy Tune Music Video" }, + entity! { __typename: "Photo", id: md[3], title: "Rock Tune Single Cover" }, + entity! { __typename: "Video", id: md[4], title: "Rock Tune Music Video" }, + entity! { __typename: "Photo", id: md[5], title: "Pop Tune Single Cover" }, + entity! { __typename: "Video", id: md[6], title: "Folk Tune Music Video" }, + entity! { __typename: "Album", id: "rl1", title: "Pop and Folk", songs: vec![s[3], s[4]] }, + entity! { __typename: "Single", id: "rl2", title: "Rock", songs: vec![s[2]] }, + entity! { __typename: "Single", id: "rl3", title: "Cheesy", songs: vec![s[1]] }, ]; let entities1 = vec![ @@ -733,7 +785,7 @@ fn can_query_an_interface_with_child_filter_on_named_type_field() { } #[test] -fn can_query_with_child_filter_on_interface_field() { +fn can_query_with_child_filter_on_derived_interface_list_field() { const QUERY: &str = " query { users(first: 100, orderBy: id, where: { reviews_: { body_starts_with: \"Good\" } }) { @@ -756,6 +808,181 @@ fn can_query_with_child_filter_on_interface_field() { }) } +#[test] +fn can_query_entity_by_child_entity_field() { + const QUERY: &str = " + query { + users(first: 100, orderBy: id, where: { latestSongReview_: { body_starts_with: \"Good\" } }) { + name + latestSongReview { + body + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + users: vec![ + object! { name: "Goodwill", latestSongReview: object! { body: "Good" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_entity_by_child_interface_field() { + const QUERY: &str = " + query { + users(first: 100, orderBy: id, where: { latestReview_: { body_starts_with: \"Good\" } }) { + name + latestReview { + body + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + users: vec![ + object! { name: "Goodwill", latestReview: object! { body: "Good amateurs" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_interface_by_child_entity_field() { + const QUERY: &str = " + query { + reviews(first: 100, orderBy: id, where: { author_: { name_starts_with: \"Good\" } }) { + body + author { + name + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + reviews: vec![ + object! { body: "Good amateurs", author: object! { name: "Goodwill" } }, + object! { body: "Good", author: object! { name: "Goodwill" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_entity_by_child_interface_derived_field() { + const QUERY: &str = " + query { + songs(first: 100, orderBy: id, where: { release_: { title_starts_with: \"Pop\" } }) { + title + release { + title + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + songs: vec![ + object! { title: "Pop Tune", release: object! { title: "Pop and Folk" } }, + object! { title: "Folk Tune", release: object! { title: "Pop and Folk" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_interface_by_child_entity_derived_field() { + const QUERY: &str = " + query { + medias(first: 100, orderBy: id, where: { song_: { title_starts_with: \"Folk\" } }) { + title + song { + title + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + medias: vec![ + object! { title: "Folk Tune Music Video", song: object! { title: "Folk Tune" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_entity_by_child_interface_list_field() { + const QUERY: &str = " + query { + songs(first: 100, orderBy: id, where: { media_: { title_starts_with: \"Cheesy Tune\" } }) { + title + media { + title + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + songs: vec![ + object! { title: "Cheesy Tune", media: vec![ + object! { title: "Cheesy Tune Single Cover" }, + object! { title: "Cheesy Tune Music Video" } + ] }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_entity_by_child_interface_list_derived_field() { + const QUERY: &str = " + query { + songs(first: 100, orderBy: id, where: { reviews_: { body_starts_with: \"Good\" } }) { + title + reviews { + body + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + songs: vec![ + object! { title: "Pop Tune", reviews: vec![ + object! { body: "Good" }, + ] }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + #[test] fn root_fragments_are_expanded() { const QUERY: &str = r#" From c49103cc7e3e34852bfdc9928313c4d0c9d2368f Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 16 Aug 2022 12:08:09 -0700 Subject: [PATCH 004/253] node, store: Move getting statistics for a deployment into the store --- node/src/manager/commands/stats.rs | 81 +++++++++++------------------- store/postgres/src/catalog.rs | 32 ++++++++++++ store/postgres/src/lib.rs | 2 +- 3 files changed, 62 insertions(+), 53 deletions(-) diff --git a/node/src/manager/commands/stats.rs b/node/src/manager/commands/stats.rs index be8799b98b3..803efedfe83 100644 --- a/node/src/manager/commands/stats.rs +++ b/node/src/manager/commands/stats.rs @@ -5,7 +5,6 @@ use crate::manager::deployment::DeploymentSearch; use diesel::r2d2::ConnectionManager; use diesel::r2d2::PooledConnection; use diesel::sql_query; -use diesel::sql_types::{Integer, Text}; use diesel::PgConnection; use diesel::RunQueryDsl; use graph::prelude::anyhow; @@ -59,64 +58,38 @@ pub fn show( ) -> Result<(), anyhow::Error> { let (site, conn) = site_and_conn(pools, search)?; - #[derive(Queryable, QueryableByName)] - struct VersionStats { - #[sql_type = "Integer"] - entities: i32, - #[sql_type = "Integer"] - versions: i32, - #[sql_type = "Text"] - tablename: String, + fn header() { + println!( + "{:^30} | {:^10} | {:^10} | {:^7}", + "table", "entities", "versions", "ratio" + ); + println!("{:-^30}-+-{:-^10}-+-{:-^10}-+-{:-^7}", "", "", "", ""); } - impl VersionStats { - fn header() { - println!( - "{:^30} | {:^10} | {:^10} | {:^7}", - "table", "entities", "versions", "ratio" - ); - println!("{:-^30}-+-{:-^10}-+-{:-^10}-+-{:-^7}", "", "", "", ""); - } - - fn print(&self, account_like: bool) { - println!( - "{:<26} {:3} | {:>10} | {:>10} | {:>5.1}%", - self.tablename, - if account_like { "(a)" } else { " " }, - self.entities, - self.versions, - self.entities as f32 * 100.0 / self.versions as f32 - ); - } + fn footer() { + println!(" (a): account-like flag set"); + } - fn footer() { - println!(" (a): account-like flag set"); - } + fn print_stats(s: &store_catalog::VersionStats, account_like: bool) { + println!( + "{:<26} {:3} | {:>10} | {:>10} | {:>5.1}%", + s.tablename, + if account_like { "(a)" } else { " " }, + s.entities, + s.versions, + s.entities as f32 * 100.0 / s.versions as f32 + ); } - let query = format!( - "select s.n_distinct::int4 as entities, - c.reltuples::int4 as versions, - c.relname as tablename - from pg_namespace n, pg_class c, pg_stats s - where n.nspname = $1 - and c.relnamespace = n.oid - and s.schemaname = n.nspname - and s.attname = 'id' - and c.relname = s.tablename - order by c.relname" - ); - let stats = sql_query(query) - .bind::(&site.namespace.as_str()) - .load::(&conn)?; + let stats = store_catalog::stats(&conn, &site.namespace)?; let account_like = store_catalog::account_like(&conn, &site)?; - VersionStats::header(); - for stat in &stats { - stat.print(account_like.contains(&stat.tablename)); + header(); + for s in &stats { + print_stats(s, account_like.contains(&s.tablename)); } - VersionStats::footer(); + footer(); if let Some(table) = table { if !stats.iter().any(|stat| stat.tablename == table) { @@ -136,8 +109,12 @@ pub fn show( nsp = &site.namespace, table = table ); - let stat = sql_query(query).get_result::(&conn)?; - stat.print(account_like.contains(&stat.tablename)); + // Using `VersionStats` this way is a little dangerous since it + // couples the query in store_catalog to this query, but since this + // is in a rarely used option to `stats show`, we'll live with the + // risk + let stat = sql_query(query).get_result::(&conn)?; + print_stats(&stat, account_like.contains(&stat.tablename)); } Ok(()) diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 3a9de92bf66..5987fe35437 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -501,3 +501,35 @@ pub(crate) fn drop_index( .map_err::(Into::into)?; Ok(()) } + +/// An estimate of the number of entities and the number of entity versions +/// in a database table +#[derive(Clone, Debug, Queryable, QueryableByName)] +pub struct VersionStats { + #[sql_type = "Integer"] + pub entities: i32, + #[sql_type = "Integer"] + pub versions: i32, + #[sql_type = "Text"] + pub tablename: String, +} + +pub fn stats(conn: &PgConnection, namespace: &Namespace) -> Result, StoreError> { + let query = format!( + "select s.n_distinct::int4 as entities, + c.reltuples::int4 as versions, + c.relname as tablename + from pg_namespace n, pg_class c, pg_stats s + where n.nspname = $1 + and c.relnamespace = n.oid + and s.schemaname = n.nspname + and s.attname = 'id' + and c.relname = s.tablename + order by c.relname" + ); + + sql_query(query) + .bind::(namespace.as_str()) + .load::(conn) + .map_err(StoreError::from) +} diff --git a/store/postgres/src/lib.rs b/store/postgres/src/lib.rs index 32c20e8b817..88c15fca796 100644 --- a/store/postgres/src/lib.rs +++ b/store/postgres/src/lib.rs @@ -69,7 +69,7 @@ pub use self::subgraph_store::{unused, DeploymentPlacer, Shard, SubgraphStore, P pub mod command_support { pub mod catalog { pub use crate::block_store::primary as block_store; - pub use crate::catalog::account_like; + pub use crate::catalog::{account_like, stats, VersionStats}; pub use crate::copy::{copy_state, copy_table_state}; pub use crate::primary::Connection; pub use crate::primary::{ From 8022eb3420531739969ac1a0c7abdf0bdd8a0b6c Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 16 Aug 2022 12:14:07 -0700 Subject: [PATCH 005/253] node: Remove the optional table argument from `stats show` That was a footgun anyway and usually just caused inadvertent counting that was slow --- node/src/bin/manager.rs | 15 ++++----------- node/src/manager/commands/stats.rs | 30 ------------------------------ 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index d6a776d6814..ce0cd84f3dc 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -399,16 +399,10 @@ pub enum StatsCommand { /// /// Show how many distinct entities and how many versions the tables of /// each subgraph have. The data is based on the statistics that - /// Postgres keeps, and only refreshed when a table is analyzed. If a - /// table name is passed, perform a full count of entities and versions - /// in that table, which can be very slow, but is needed since the - /// statistics based data can be off by an order of magnitude. + /// Postgres keeps, and only refreshed when a table is analyzed. Show { /// The deployment (see `help info`). deployment: DeploymentSearch, - /// The name of a table to fully count which can be very slow - #[structopt(long, short)] - table: Option, }, /// Perform a SQL ANALYZE in a Entity table Analyze { @@ -989,8 +983,9 @@ async fn main() -> anyhow::Result<()> { table, } => { let (store, primary_pool) = ctx.store_and_primary(); + let subgraph_store = store.subgraph_store(); commands::stats::account_like( - store.subgraph_store(), + subgraph_store, primary_pool, clear, &deployment, @@ -998,9 +993,7 @@ async fn main() -> anyhow::Result<()> { ) .await } - Show { deployment, table } => { - commands::stats::show(ctx.pools(), &deployment, table) - } + Show { deployment } => commands::stats::show(ctx.pools(), &deployment), Analyze { deployment, entity } => { let (store, primary_pool) = ctx.store_and_primary(); let subgraph_store = store.subgraph_store(); diff --git a/node/src/manager/commands/stats.rs b/node/src/manager/commands/stats.rs index 803efedfe83..2b60910aaa4 100644 --- a/node/src/manager/commands/stats.rs +++ b/node/src/manager/commands/stats.rs @@ -4,11 +4,8 @@ use std::sync::Arc; use crate::manager::deployment::DeploymentSearch; use diesel::r2d2::ConnectionManager; use diesel::r2d2::PooledConnection; -use diesel::sql_query; use diesel::PgConnection; -use diesel::RunQueryDsl; use graph::prelude::anyhow; -use graph::prelude::anyhow::bail; use graph_store_postgres::command_support::catalog as store_catalog; use graph_store_postgres::command_support::catalog::Site; use graph_store_postgres::connection_pool::ConnectionPool; @@ -54,7 +51,6 @@ pub async fn account_like( pub fn show( pools: HashMap, search: &DeploymentSearch, - table: Option, ) -> Result<(), anyhow::Error> { let (site, conn) = site_and_conn(pools, search)?; @@ -91,32 +87,6 @@ pub fn show( } footer(); - if let Some(table) = table { - if !stats.iter().any(|stat| stat.tablename == table) { - bail!( - "deployment {} does not have a table `{}`", - site.namespace, - table - ); - } - - println!("doing a full count on {}.{} ...", site.namespace, table); - let query = format!( - "select count(distinct id)::int4 as entities, - count(*)::int4 as versions, - '{table}' as tablename - from {nsp}.{table}", - nsp = &site.namespace, - table = table - ); - // Using `VersionStats` this way is a little dangerous since it - // couples the query in store_catalog to this query, but since this - // is in a rarely used option to `stats show`, we'll live with the - // risk - let stat = sql_query(query).get_result::(&conn)?; - print_stats(&stat, account_like.contains(&stat.tablename)); - } - Ok(()) } From 2c2ae92b2cd820e818565919196e9256365f7505 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 16 Aug 2022 12:36:30 -0700 Subject: [PATCH 006/253] node, store: Move calculation of entities/version ratio to store Also, fix handling of negative n_distinct value --- node/src/manager/commands/stats.rs | 2 +- store/postgres/src/catalog.rs | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/node/src/manager/commands/stats.rs b/node/src/manager/commands/stats.rs index 2b60910aaa4..8e382b40d6f 100644 --- a/node/src/manager/commands/stats.rs +++ b/node/src/manager/commands/stats.rs @@ -73,7 +73,7 @@ pub fn show( if account_like { "(a)" } else { " " }, s.entities, s.versions, - s.entities as f32 * 100.0 / s.versions as f32 + s.ratio * 100.0 ); } diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 5987fe35437..3bcb6b287e5 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -3,7 +3,7 @@ use diesel::{connection::SimpleConnection, prelude::RunQueryDsl, select}; use diesel::{insert_into, OptionalExtension}; use diesel::{pg::PgConnection, sql_query}; use diesel::{ - sql_types::{Array, Nullable, Text}, + sql_types::{Array, Double, Nullable, Text}, ExpressionMethods, QueryDsl, }; use std::collections::{HashMap, HashSet}; @@ -512,13 +512,27 @@ pub struct VersionStats { pub versions: i32, #[sql_type = "Text"] pub tablename: String, + /// The ratio `entities / versions` + #[sql_type = "Double"] + pub ratio: f64, } pub fn stats(conn: &PgConnection, namespace: &Namespace) -> Result, StoreError> { + // Get an estimate of number of rows (pg_class.reltuples) and number of + // distinct entities (based on the planners idea of how many distinct + // values there are in the `id` column) See the [Postgres + // docs](https://www.postgresql.org/docs/current/view-pg-stats.html) for + // the precise meaning of n_distinct let query = format!( - "select s.n_distinct::int4 as entities, - c.reltuples::int4 as versions, - c.relname as tablename + "select case when s.n_distinct < 0 then (- s.n_distinct * c.reltuples)::int4 + else s.n_distinct::int4 + end as entities, + c.reltuples::int4 as versions, + c.relname as tablename, + case when c.reltuples = 0 then 0::float8 + when s.n_distinct < 0 then (-s.n_distinct)::float8 + else greatest(s.n_distinct, 1)::float8 / c.reltuples::float8 + end as ratio from pg_namespace n, pg_class c, pg_stats s where n.nspname = $1 and c.relnamespace = n.oid From 0dfb3df676b4e8737ac6c4de7c9e84de8bbd32b9 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 16 Aug 2022 17:21:31 -0700 Subject: [PATCH 007/253] store: Move running 'analyze' into Table --- store/postgres/src/deployment_store.rs | 5 +---- store/postgres/src/relational.rs | 7 +++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index 9f21873674e..3ff624738c8 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -698,10 +698,7 @@ impl DeploymentStore { let entity_name = entity_name.to_owned(); let layout = store.layout(&conn, site)?; let table = resolve_table_name(&layout, &entity_name)?; - let table_name = &table.qualified_name; - let sql = format!("analyze {table_name}"); - conn.execute(&sql)?; - Ok(()) + table.analyze(conn) } /// Creates a new index in the specified Entity table if it doesn't already exist. diff --git a/store/postgres/src/relational.rs b/store/postgres/src/relational.rs index a71639a9e42..141501490b4 100644 --- a/store/postgres/src/relational.rs +++ b/store/postgres/src/relational.rs @@ -1290,6 +1290,13 @@ impl Table { .find(|column| column.is_primary_key()) .expect("every table has a primary key") } + + pub(crate) fn analyze(&self, conn: &PgConnection) -> Result<(), StoreError> { + let table_name = &self.qualified_name; + let sql = format!("analyze {table_name}"); + conn.execute(&sql)?; + Ok(()) + } } /// Return the enclosed named type for a field type, i.e., the type after From f9fb02c542b3b31444710f0038c8bd236e60aa33 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 19 Aug 2022 16:17:43 -0700 Subject: [PATCH 008/253] store: Decouple copying in batches from how copy state is stored --- store/postgres/src/copy.rs | 163 +++++++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 63 deletions(-) diff --git a/store/postgres/src/copy.rs b/store/postgres/src/copy.rs index 9f8658098d5..cb74d094b1a 100644 --- a/store/postgres/src/copy.rs +++ b/store/postgres/src/copy.rs @@ -202,17 +202,17 @@ impl CopyState { }) }) .collect::>()?; - tables.sort_by_key(|table| table.dst.object.to_string()); + tables.sort_by_key(|table| table.batch.dst.object.to_string()); let values = tables .iter() .map(|table| { ( - cts::entity_type.eq(table.dst.object.as_str()), + cts::entity_type.eq(table.batch.dst.object.as_str()), cts::dst.eq(dst.site.id), - cts::next_vid.eq(table.next_vid), - cts::target_vid.eq(table.target_vid), - cts::batch_size.eq(table.batch_size), + cts::next_vid.eq(table.batch.next_vid), + cts::target_vid.eq(table.batch.target_vid), + cts::batch_size.eq(table.batch.batch_size), ) }) .collect::>(); @@ -268,14 +268,73 @@ impl CopyState { } } -struct TableState { - dst_site: Arc, +/// A helper to copy entities from one table to another in batches that are +/// small enough to not interfere with the rest of the operations happening +/// in the database. The `src` and `dst` table must have the same structure +/// so that we can copy rows from one to the other with very little +/// transformation. See `CopyEntityBatchQuery` for the details of what +/// exactly that means +pub(crate) struct BatchCopy { src: Arc, dst: Arc
, /// The `vid` of the next entity version that we will copy next_vid: i64, + /// The last `vid` that should be copied target_vid: i64, batch_size: i64, +} + +impl BatchCopy { + pub fn new(src: Arc
, dst: Arc
, first_vid: i64, last_vid: i64) -> Self { + let batch_size = if dst.columns.iter().any(|col| col.is_list()) { + INITIAL_BATCH_SIZE_LIST + } else { + INITIAL_BATCH_SIZE + }; + + Self { + src, + dst, + next_vid: first_vid, + target_vid: last_vid, + batch_size, + } + } + + /// Copy one batch of entities and update internal state so that the + /// next call to `run` will copy the next batch + pub fn run(&mut self, conn: &PgConnection) -> Result { + let start = Instant::now(); + + // Copy all versions with next_vid <= vid <= next_vid + batch_size - 1, + // but do not go over target_vid + let last_vid = (self.next_vid + self.batch_size - 1).min(self.target_vid); + rq::CopyEntityBatchQuery::new(self.dst.as_ref(), &self.src, self.next_vid, last_vid)? + .execute(conn)?; + + let duration = start.elapsed(); + + // remember how far we got + self.next_vid = last_vid + 1; + + // adjust batch size by trying to extrapolate in such a way that we + // get close to TARGET_DURATION for the time it takes to copy one + // batch, but don't step up batch_size by more than 2x at once + let new_batch_size = self.batch_size as f64 * TARGET_DURATION.as_millis() as f64 + / duration.as_millis() as f64; + self.batch_size = (2 * self.batch_size).min(new_batch_size.round() as i64); + + Ok(duration) + } + + pub fn finished(&self) -> bool { + self.next_vid > self.target_vid + } +} + +struct TableState { + batch: BatchCopy, + dst_site: Arc, duration_ms: i64, } @@ -309,25 +368,15 @@ impl TableState { .map(|v| v.max_vid) .unwrap_or(-1); - let batch_size = if dst.columns.iter().any(|col| col.is_list()) { - INITIAL_BATCH_SIZE_LIST - } else { - INITIAL_BATCH_SIZE - }; - Ok(Self { + batch: BatchCopy::new(src, dst, 0, target_vid), dst_site, - src, - dst, - next_vid: 0, - target_vid, - batch_size, duration_ms: 0, }) } fn finished(&self) -> bool { - self.next_vid > self.target_vid + self.batch.finished() } fn load( @@ -385,15 +434,16 @@ impl TableState { id, ); match (src, dst) { - (Ok(src), Ok(dst)) => Ok(TableState { - dst_site: dst_layout.site.clone(), - src, - dst, - next_vid: current_vid, - target_vid, - batch_size, - duration_ms, - }), + (Ok(src), Ok(dst)) => { + let mut batch = BatchCopy::new(src, dst, current_vid, target_vid); + batch.batch_size = batch_size; + + Ok(TableState { + batch, + dst_site: dst_layout.site.clone(), + duration_ms, + }) + } (Err(e), _) => Err(e), (_, Err(e)) => Err(e), } @@ -420,20 +470,20 @@ impl TableState { update( cts::table .filter(cts::dst.eq(self.dst_site.id)) - .filter(cts::entity_type.eq(self.dst.object.as_str())), + .filter(cts::entity_type.eq(self.batch.dst.object.as_str())), ) .set(cts::started_at.eq(sql("now()"))) .execute(conn)?; } let values = ( - cts::next_vid.eq(self.next_vid), - cts::batch_size.eq(self.batch_size), + cts::next_vid.eq(self.batch.next_vid), + cts::batch_size.eq(self.batch.batch_size), cts::duration_ms.eq(self.duration_ms), ); update( cts::table .filter(cts::dst.eq(self.dst_site.id)) - .filter(cts::entity_type.eq(self.dst.object.as_str())), + .filter(cts::entity_type.eq(self.batch.dst.object.as_str())), ) .set(values) .execute(conn)?; @@ -446,7 +496,7 @@ impl TableState { update( cts::table .filter(cts::dst.eq(self.dst_site.id)) - .filter(cts::entity_type.eq(self.dst.object.as_str())), + .filter(cts::entity_type.eq(self.batch.dst.object.as_str())), ) .set(cts::finished_at.eq(sql("now()"))) .execute(conn)?; @@ -472,26 +522,9 @@ impl TableState { } fn copy_batch(&mut self, conn: &PgConnection) -> Result { - let start = Instant::now(); + let first_batch = self.batch.next_vid == 0; - // Copy all versions with next_vid <= vid <= next_vid + batch_size - 1, - // but do not go over target_vid - let first_batch = self.next_vid == 0; - let last_vid = (self.next_vid + self.batch_size - 1).min(self.target_vid); - rq::CopyEntityBatchQuery::new(self.dst.as_ref(), &self.src, self.next_vid, last_vid)? - .execute(conn)?; - - let duration = start.elapsed(); - - // remember how far we got - self.next_vid = last_vid + 1; - - // adjust batch size by trying to extrapolate in such a way that we - // get close to TARGET_DURATION for the time it takes to copy one - // batch, but don't step up batch_size by more than 2x at once - let new_batch_size = self.batch_size as f64 * TARGET_DURATION.as_millis() as f64 - / duration.as_millis() as f64; - self.batch_size = (2 * self.batch_size).min(new_batch_size.round() as i64); + let duration = self.batch.run(conn)?; self.record_progress(conn, duration, first_batch)?; @@ -515,7 +548,11 @@ struct CopyProgress<'a> { impl<'a> CopyProgress<'a> { fn new(logger: &'a Logger, state: &CopyState) -> Self { - let target_vid: i64 = state.tables.iter().map(|table| table.target_vid).sum(); + let target_vid: i64 = state + .tables + .iter() + .map(|table| table.batch.target_vid) + .sum(); Self { logger, last_log: Instant::now(), @@ -547,23 +584,23 @@ impl<'a> CopyProgress<'a> { } } - fn update(&mut self, table: &TableState) { + fn update(&mut self, batch: &BatchCopy) { if self.last_log.elapsed() > LOG_INTERVAL { info!( self.logger, "Copied {:.2}% of `{}` entities ({}/{} entity versions), {:.2}% of overall data", - Self::progress_pct(table.next_vid, table.target_vid), - table.dst.object, - table.next_vid, - table.target_vid, - Self::progress_pct(self.current_vid + table.next_vid, self.target_vid) + Self::progress_pct(batch.next_vid, batch.target_vid), + batch.dst.object, + batch.next_vid, + batch.target_vid, + Self::progress_pct(self.current_vid + batch.next_vid, self.target_vid) ); self.last_log = Instant::now(); } } - fn table_finished(&mut self, table: &TableState) { - self.current_vid += table.next_vid; + fn table_finished(&mut self, batch: &BatchCopy) { + self.current_vid += batch.next_vid; } fn finished(&self) { @@ -669,9 +706,9 @@ impl Connection { if status == Status::Cancelled { return Ok(status); } - progress.update(table); + progress.update(&table.batch); } - progress.table_finished(table); + progress.table_finished(&table.batch); } self.copy_private_data_sources(&state)?; From e438de797d9c122a0a7c32a983b7f6b8e1e8deb9 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 23 Aug 2022 15:45:56 -0700 Subject: [PATCH 009/253] store: Extract controlling batch size for copying into a helper --- store/postgres/src/copy.rs | 76 +++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/store/postgres/src/copy.rs b/store/postgres/src/copy.rs index cb74d094b1a..7b0e5efdc43 100644 --- a/store/postgres/src/copy.rs +++ b/store/postgres/src/copy.rs @@ -13,6 +13,7 @@ //! `graph-node` was restarted while the copy was running. use std::{ convert::TryFrom, + io::Write, sync::Arc, time::{Duration, Instant}, }; @@ -20,9 +21,13 @@ use std::{ use diesel::{ dsl::sql, insert_into, + pg::Pg, r2d2::{ConnectionManager, PooledConnection}, - select, sql_query, - sql_types::Integer, + select, + serialize::Output, + sql_query, + sql_types::{BigInt, Integer}, + types::{FromSql, ToSql}, update, Connection as _, ExpressionMethods, OptionalExtension, PgConnection, QueryDsl, RunQueryDsl, }; @@ -212,7 +217,7 @@ impl CopyState { cts::dst.eq(dst.site.id), cts::next_vid.eq(table.batch.next_vid), cts::target_vid.eq(table.batch.target_vid), - cts::batch_size.eq(table.batch.batch_size), + cts::batch_size.eq(table.batch.batch_size.size), ) }) .collect::>(); @@ -268,6 +273,48 @@ impl CopyState { } } +/// Track the desired size of a batch in such a way that doing the next +/// batch gets close to TARGET_DURATION for the time it takes to copy one +/// batch, but don't step up the size by more than 2x at once +#[derive(Debug, Queryable)] +pub(crate) struct AdaptiveBatchSize { + size: i64, +} + +impl AdaptiveBatchSize { + pub fn new(table: &Table) -> Self { + let size = if table.columns.iter().any(|col| col.is_list()) { + INITIAL_BATCH_SIZE_LIST + } else { + INITIAL_BATCH_SIZE + }; + + Self { size } + } + + // adjust batch size by trying to extrapolate in such a way that we + // get close to TARGET_DURATION for the time it takes to copy one + // batch, but don't step up batch_size by more than 2x at once + pub fn adapt(&mut self, duration: Duration) { + let new_batch_size = + self.size as f64 * TARGET_DURATION.as_millis() as f64 / duration.as_millis() as f64; + self.size = (2 * self.size).min(new_batch_size.round() as i64); + } +} + +impl ToSql for AdaptiveBatchSize { + fn to_sql(&self, out: &mut Output) -> diesel::serialize::Result { + >::to_sql(&self.size, out) + } +} + +impl FromSql for AdaptiveBatchSize { + fn from_sql(bytes: Option<&[u8]>) -> diesel::deserialize::Result { + let size = >::from_sql(bytes)?; + Ok(AdaptiveBatchSize { size }) + } +} + /// A helper to copy entities from one table to another in batches that are /// small enough to not interfere with the rest of the operations happening /// in the database. The `src` and `dst` table must have the same structure @@ -281,16 +328,12 @@ pub(crate) struct BatchCopy { next_vid: i64, /// The last `vid` that should be copied target_vid: i64, - batch_size: i64, + batch_size: AdaptiveBatchSize, } impl BatchCopy { pub fn new(src: Arc
, dst: Arc
, first_vid: i64, last_vid: i64) -> Self { - let batch_size = if dst.columns.iter().any(|col| col.is_list()) { - INITIAL_BATCH_SIZE_LIST - } else { - INITIAL_BATCH_SIZE - }; + let batch_size = AdaptiveBatchSize::new(&dst); Self { src, @@ -308,7 +351,7 @@ impl BatchCopy { // Copy all versions with next_vid <= vid <= next_vid + batch_size - 1, // but do not go over target_vid - let last_vid = (self.next_vid + self.batch_size - 1).min(self.target_vid); + let last_vid = (self.next_vid + self.batch_size.size - 1).min(self.target_vid); rq::CopyEntityBatchQuery::new(self.dst.as_ref(), &self.src, self.next_vid, last_vid)? .execute(conn)?; @@ -317,12 +360,7 @@ impl BatchCopy { // remember how far we got self.next_vid = last_vid + 1; - // adjust batch size by trying to extrapolate in such a way that we - // get close to TARGET_DURATION for the time it takes to copy one - // batch, but don't step up batch_size by more than 2x at once - let new_batch_size = self.batch_size as f64 * TARGET_DURATION.as_millis() as f64 - / duration.as_millis() as f64; - self.batch_size = (2 * self.batch_size).min(new_batch_size.round() as i64); + self.batch_size.adapt(duration); Ok(duration) } @@ -422,7 +460,7 @@ impl TableState { .load::<(i32, String, i64, i64, i64, i64)>(conn)? .into_iter() .map( - |(id, entity_type, current_vid, target_vid, batch_size, duration_ms)| { + |(id, entity_type, current_vid, target_vid, size, duration_ms)| { let entity_type = EntityType::new(entity_type); let src = resolve_entity(src_layout, "source", &entity_type, dst_layout.site.id, id); @@ -436,6 +474,8 @@ impl TableState { match (src, dst) { (Ok(src), Ok(dst)) => { let mut batch = BatchCopy::new(src, dst, current_vid, target_vid); + let batch_size = AdaptiveBatchSize { size }; + batch.batch_size = batch_size; Ok(TableState { @@ -477,7 +517,7 @@ impl TableState { } let values = ( cts::next_vid.eq(self.batch.next_vid), - cts::batch_size.eq(self.batch.batch_size), + cts::batch_size.eq(self.batch.batch_size.size), cts::duration_ms.eq(self.duration_ms), ); update( From 06be50880c221e2cd4128943d42071daeff17288 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 23 Aug 2022 17:07:27 -0700 Subject: [PATCH 010/253] store: Make individual steps for table creation accessible --- store/postgres/src/relational/ddl.rs | 345 +++++++++++++-------------- 1 file changed, 168 insertions(+), 177 deletions(-) diff --git a/store/postgres/src/relational/ddl.rs b/store/postgres/src/relational/ddl.rs index 22db3769435..82ea9176053 100644 --- a/store/postgres/src/relational/ddl.rs +++ b/store/postgres/src/relational/ddl.rs @@ -54,12 +54,7 @@ impl Layout { } impl Table { - /// Generate the DDL for one table, i.e. one `create table` statement - /// and all `create index` statements for the table's columns - /// - /// See the unit tests at the end of this file for the actual DDL that - /// gets generated - fn as_ddl(&self, out: &mut String, layout: &Layout) -> fmt::Result { + fn create_table(&self, out: &mut String, layout: &Layout) -> fmt::Result { fn columns_ddl(table: &Table) -> Result { let mut cols = String::new(); let mut first = true; @@ -76,190 +71,186 @@ impl Table { Ok(cols) } - fn create_table(table: &Table, out: &mut String, layout: &Layout) -> fmt::Result { - if table.immutable { - writeln!( - out, - r#" - create table {nsp}.{name} ( - {vid} bigserial primary key, - {block} int not null, - {cols}, - unique({id}) - ); - "#, - nsp = layout.catalog.site.namespace, - name = table.name.quoted(), - cols = columns_ddl(table)?, - vid = VID_COLUMN, - block = BLOCK_COLUMN, - id = table.primary_key().name - ) - } else { - writeln!( - out, - r#" - create table {nsp}.{name} ( - {vid} bigserial primary key, - {block_range} int4range not null, - {cols} - ); - "#, - nsp = layout.catalog.site.namespace, - name = table.name.quoted(), - cols = columns_ddl(table)?, - vid = VID_COLUMN, - block_range = BLOCK_RANGE_COLUMN - )?; + if self.immutable { + writeln!( + out, + r#" + create table {nsp}.{name} ( + {vid} bigserial primary key, + {block} int not null, + {cols}, + unique({id}) + ); + "#, + nsp = layout.catalog.site.namespace, + name = self.name.quoted(), + cols = columns_ddl(self)?, + vid = VID_COLUMN, + block = BLOCK_COLUMN, + id = self.primary_key().name + ) + } else { + writeln!( + out, + r#" + create table {nsp}.{name} ( + {vid} bigserial primary key, + {block_range} int4range not null, + {cols} + ); + "#, + nsp = layout.catalog.site.namespace, + name = self.name.quoted(), + cols = columns_ddl(self)?, + vid = VID_COLUMN, + block_range = BLOCK_RANGE_COLUMN + )?; - table.exclusion_ddl( - out, - layout.catalog.site.namespace.as_str(), - layout.catalog.create_exclusion_constraint(), - ) - } + self.exclusion_ddl( + out, + layout.catalog.site.namespace.as_str(), + layout.catalog.create_exclusion_constraint(), + ) } + } - fn create_time_travel_indexes( - table: &Table, - out: &mut String, - layout: &Layout, - ) -> fmt::Result { - if table.immutable { - write!( - out, - "create index brin_{table_name}\n \ - on {schema_name}.{table_name}\n \ - using brin({block}, vid);\n", - table_name = table.name, - schema_name = layout.catalog.site.namespace, - block = BLOCK_COLUMN - ) - } else { - // Add a BRIN index on the block_range bounds to exploit the fact - // that block ranges closely correlate with where in a table an - // entity appears physically. This index is incredibly efficient for - // reverts where we look for very recent blocks, so that this index - // is highly selective. See https://github.com/graphprotocol/graph-node/issues/1415#issuecomment-630520713 - // for details on one experiment. - // - // We do not index the `block_range` as a whole, but rather the lower - // and upper bound separately, since experimentation has shown that - // Postgres will not use the index on `block_range` for clauses like - // `block_range @> $block` but rather falls back to a full table scan. - // - // We also make sure that we do not put `NULL` in the index for - // the upper bound since nulls can not be compared to anything and - // will make the index less effective. - // - // To make the index usable, queries need to have clauses using - // `lower(block_range)` and `coalesce(..)` verbatim. - // - // We also index `vid` as that correlates with the order in which - // entities are stored. - write!(out,"create index brin_{table_name}\n \ - on {schema_name}.{table_name}\n \ - using brin(lower(block_range), coalesce(upper(block_range), {block_max}), vid);\n", - table_name = table.name, - schema_name = layout.catalog.site.namespace, - block_max = BLOCK_NUMBER_MAX)?; + fn create_time_travel_indexes(&self, out: &mut String, layout: &Layout) -> fmt::Result { + if self.immutable { + write!( + out, + "create index brin_{table_name}\n \ + on {schema_name}.{table_name}\n \ + using brin({block}, vid);\n", + table_name = self.name, + schema_name = layout.catalog.site.namespace, + block = BLOCK_COLUMN + ) + } else { + // Add a BRIN index on the block_range bounds to exploit the fact + // that block ranges closely correlate with where in a table an + // entity appears physically. This index is incredibly efficient for + // reverts where we look for very recent blocks, so that this index + // is highly selective. See https://github.com/graphprotocol/graph-node/issues/1415#issuecomment-630520713 + // for details on one experiment. + // + // We do not index the `block_range` as a whole, but rather the lower + // and upper bound separately, since experimentation has shown that + // Postgres will not use the index on `block_range` for clauses like + // `block_range @> $block` but rather falls back to a full table scan. + // + // We also make sure that we do not put `NULL` in the index for + // the upper bound since nulls can not be compared to anything and + // will make the index less effective. + // + // To make the index usable, queries need to have clauses using + // `lower(block_range)` and `coalesce(..)` verbatim. + // + // We also index `vid` as that correlates with the order in which + // entities are stored. + write!(out,"create index brin_{table_name}\n \ + on {schema_name}.{table_name}\n \ + using brin(lower(block_range), coalesce(upper(block_range), {block_max}), vid);\n", + table_name = self.name, + schema_name = layout.catalog.site.namespace, + block_max = BLOCK_NUMBER_MAX)?; - // Add a BTree index that helps with the `RevertClampQuery` by making - // it faster to find entity versions that have been modified - write!( - out, - "create index {table_name}_block_range_closed\n \ - on {schema_name}.{table_name}(coalesce(upper(block_range), {block_max}))\n \ - where coalesce(upper(block_range), {block_max}) < {block_max};\n", - table_name = table.name, - schema_name = layout.catalog.site.namespace, - block_max = BLOCK_NUMBER_MAX - ) - } + // Add a BTree index that helps with the `RevertClampQuery` by making + // it faster to find entity versions that have been modified + write!( + out, + "create index {table_name}_block_range_closed\n \ + on {schema_name}.{table_name}(coalesce(upper(block_range), {block_max}))\n \ + where coalesce(upper(block_range), {block_max}) < {block_max};\n", + table_name = self.name, + schema_name = layout.catalog.site.namespace, + block_max = BLOCK_NUMBER_MAX + ) } + } - fn create_attribute_indexes( - table: &Table, - out: &mut String, - layout: &Layout, - ) -> fmt::Result { - // Create indexes. Skip columns whose type is an array of enum, - // since there is no good way to index them with Postgres 9.6. - // Once we move to Postgres 11, we can enable that - // (tracked in graph-node issue #1330) - for (i, column) in table - .columns - .iter() - .filter(|col| !(col.is_list() && col.is_enum())) - .enumerate() - { - if table.immutable && column.is_primary_key() { - // We create a unique index on `id` in `create_table` - // and don't need an explicit attribute index - continue; - } + fn create_attribute_indexes(&self, out: &mut String, layout: &Layout) -> fmt::Result { + // Create indexes. Skip columns whose type is an array of enum, + // since there is no good way to index them with Postgres 9.6. + // Once we move to Postgres 11, we can enable that + // (tracked in graph-node issue #1330) + for (i, column) in self + .columns + .iter() + .filter(|col| !(col.is_list() && col.is_enum())) + .enumerate() + { + if self.immutable && column.is_primary_key() { + // We create a unique index on `id` in `create_table` + // and don't need an explicit attribute index + continue; + } - let (method, index_expr) = if column.is_reference() && !column.is_list() { - // For foreign keys, index the key together with the block range - // since we almost always also have a block_range clause in - // queries that look for specific foreign keys - if table.immutable { - let index_expr = format!("{}, {}", column.name.quoted(), BLOCK_COLUMN); - ("btree", index_expr) - } else { - let index_expr = - format!("{}, {}", column.name.quoted(), BLOCK_RANGE_COLUMN); - ("gist", index_expr) - } + let (method, index_expr) = if column.is_reference() && !column.is_list() { + // For foreign keys, index the key together with the block range + // since we almost always also have a block_range clause in + // queries that look for specific foreign keys + if self.immutable { + let index_expr = format!("{}, {}", column.name.quoted(), BLOCK_COLUMN); + ("btree", index_expr) } else { - // Attributes that are plain strings or bytes are - // indexed with a BTree; but they can be too large for - // Postgres' limit on values that can go into a BTree. - // For those attributes, only index the first - // STRING_PREFIX_SIZE or BYTE_ARRAY_PREFIX_SIZE characters - // see: attr-bytea-prefix - let index_expr = if column.use_prefix_comparison { - match column.column_type { - ColumnType::String => { - format!("left({}, {})", column.name.quoted(), STRING_PREFIX_SIZE) - } - ColumnType::Bytes => format!( - "substring({}, 1, {})", - column.name.quoted(), - BYTE_ARRAY_PREFIX_SIZE - ), - _ => unreachable!("only String and Bytes can have arbitrary size"), + let index_expr = format!("{}, {}", column.name.quoted(), BLOCK_RANGE_COLUMN); + ("gist", index_expr) + } + } else { + // Attributes that are plain strings or bytes are + // indexed with a BTree; but they can be too large for + // Postgres' limit on values that can go into a BTree. + // For those attributes, only index the first + // STRING_PREFIX_SIZE or BYTE_ARRAY_PREFIX_SIZE characters + // see: attr-bytea-prefix + let index_expr = if column.use_prefix_comparison { + match column.column_type { + ColumnType::String => { + format!("left({}, {})", column.name.quoted(), STRING_PREFIX_SIZE) } - } else { - column.name.quoted() - }; - - let method = if column.is_list() || column.is_fulltext() { - "gin" - } else { - "btree" - }; + ColumnType::Bytes => format!( + "substring({}, 1, {})", + column.name.quoted(), + BYTE_ARRAY_PREFIX_SIZE + ), + _ => unreachable!("only String and Bytes can have arbitrary size"), + } + } else { + column.name.quoted() + }; - (method, index_expr) + let method = if column.is_list() || column.is_fulltext() { + "gin" + } else { + "btree" }; - write!( - out, - "create index attr_{table_index}_{column_index}_{table_name}_{column_name}\n on {schema_name}.\"{table_name}\" using {method}({index_expr});\n", - table_index = table.position, - table_name = table.name, - column_index = i, - column_name = column.name, - schema_name = layout.catalog.site.namespace, - method = method, - index_expr = index_expr, - )?; - } - writeln!(out) + + (method, index_expr) + }; + write!( + out, + "create index attr_{table_index}_{column_index}_{table_name}_{column_name}\n on {schema_name}.\"{table_name}\" using {method}({index_expr});\n", + table_index = self.position, + table_name = self.name, + column_index = i, + column_name = column.name, + schema_name = layout.catalog.site.namespace, + method = method, + index_expr = index_expr, + )?; } + writeln!(out) + } - create_table(self, out, layout)?; - create_time_travel_indexes(self, out, layout)?; - create_attribute_indexes(self, out, layout) + /// Generate the DDL for one table, i.e. one `create table` statement + /// and all `create index` statements for the table's columns + /// + /// See the unit tests at the end of this file for the actual DDL that + /// gets generated + fn as_ddl(&self, out: &mut String, layout: &Layout) -> fmt::Result { + self.create_table(out, layout)?; + self.create_time_travel_indexes(out, layout)?; + self.create_attribute_indexes(out, layout) } pub fn exclusion_ddl(&self, out: &mut String, nsp: &str, as_constraint: bool) -> fmt::Result { From 686bdbe204ddd6833c57334524474df53e20d55d Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 31 Aug 2022 09:56:05 -0700 Subject: [PATCH 011/253] graph, store: Add SubgraphStore.prune --- graph/src/components/store/mod.rs | 19 ++ store/postgres/src/catalog.rs | 69 ++++- store/postgres/src/deployment.rs | 28 ++ store/postgres/src/deployment_store.rs | 64 ++++- store/postgres/src/relational.rs | 18 ++ store/postgres/src/relational/ddl.rs | 101 ++++++- store/postgres/src/relational/ddl_tests.rs | 31 ++ store/postgres/src/relational/prune.rs | 318 +++++++++++++++++++++ store/postgres/src/subgraph_store.rs | 40 ++- store/postgres/tests/graft.rs | 134 +++++++-- 10 files changed, 785 insertions(+), 37 deletions(-) create mode 100644 store/postgres/src/relational/prune.rs diff --git a/graph/src/components/store/mod.rs b/graph/src/components/store/mod.rs index eba7f9fab82..88fb1d65f10 100644 --- a/graph/src/components/store/mod.rs +++ b/graph/src/components/store/mod.rs @@ -1076,3 +1076,22 @@ impl ReadStore for EmptyStore { self.schema.cheap_clone() } } + +/// Callbacks for `SubgraphStore.prune` so that callers can report progress +/// of the pruning procedure to users +#[allow(unused_variables)] +pub trait PruneReporter: Send + 'static { + fn start_analyze(&mut self, table: &str) {} + fn finish_analyze(&mut self, table: &str) {} + + fn copy_final_start(&mut self, earliest_block: BlockNumber, final_block: BlockNumber) {} + fn copy_final_batch(&mut self, table: &str, rows: usize, total_rows: usize, finished: bool) {} + fn copy_final_finish(&mut self) {} + + fn start_switch(&mut self) {} + fn copy_nonfinal_start(&mut self, table: &str) {} + fn copy_nonfinal_finish(&mut self, table: &str, rows: usize) {} + fn finish_switch(&mut self) {} + + fn finish_prune(&mut self) {} +} diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 3bcb6b287e5..dd08d53ffc4 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -12,7 +12,10 @@ use std::iter::FromIterator; use std::sync::Arc; use graph::prelude::anyhow::anyhow; -use graph::{data::subgraph::schema::POI_TABLE, prelude::StoreError}; +use graph::{ + data::subgraph::schema::POI_TABLE, + prelude::{lazy_static, StoreError}, +}; use crate::connection_pool::ForeignServer; use crate::{ @@ -119,7 +122,7 @@ impl Catalog { /// Whether to create exclusion indexes; if false, create gist indexes /// w/o an exclusion constraint - pub fn create_exclusion_constraint(&self) -> bool { + pub fn create_exclusion_constraint() -> bool { CREATE_EXCLUSION_CONSTRAINT } } @@ -154,9 +157,10 @@ fn get_text_columns( Ok(map) } -pub fn supports_proof_of_indexing( - conn: &diesel::pg::PgConnection, +pub fn table_exists( + conn: &PgConnection, namespace: &Namespace, + table: &SqlName, ) -> Result { #[derive(Debug, QueryableByName)] struct Table { @@ -167,12 +171,65 @@ pub fn supports_proof_of_indexing( let query = "SELECT table_name FROM information_schema.tables WHERE table_schema=$1 AND table_name=$2"; let result: Vec
= diesel::sql_query(query) - .bind::(namespace.as_str()) - .bind::(POI_TABLE) + .bind::(namespace) + .bind::(table.as_str()) .load(conn)?; Ok(result.len() > 0) } +pub fn supports_proof_of_indexing( + conn: &diesel::pg::PgConnection, + namespace: &Namespace, +) -> Result { + lazy_static! { + static ref POI_TABLE_NAME: SqlName = SqlName::verbatim(POI_TABLE.to_owned()); + } + table_exists(conn, namespace, &POI_TABLE_NAME) +} + +/// Whether the given table has an exclusion constraint. When we create +/// tables, they either have an exclusion constraint on `(id, block_range)`, +/// or just a GIST index on those columns. If this returns `true`, there is +/// an exclusion constraint on the table, if it returns `false` we only have +/// an index. +/// +/// This function only checks whether there is some exclusion constraint on +/// the table since checking fully if that is exactly the constraint we +/// think it is is a bit more complex. But if the table is part of a +/// deployment that we created, the conclusions in hte previous paragraph +/// are true. +pub fn has_exclusion_constraint( + conn: &PgConnection, + namespace: &Namespace, + table: &SqlName, +) -> Result { + #[derive(Debug, QueryableByName)] + struct Row { + #[sql_type = "Bool"] + #[allow(dead_code)] + uses_excl: bool, + } + + let query = " + select count(*) > 0 as uses_excl + from pg_catalog.pg_constraint con, + pg_catalog.pg_class rel, + pg_catalog.pg_namespace nsp + where rel.oid = con.conrelid + and nsp.oid = con.connamespace + and con.contype = 'x' + and nsp.nspname = $1 + and rel.relname = $2; + "; + + sql_query(query) + .bind::(namespace) + .bind::(table.as_str()) + .get_result::(conn) + .map_err(StoreError::from) + .map(|row| row.uses_excl) +} + pub fn current_servers(conn: &PgConnection) -> Result, StoreError> { #[derive(QueryableByName)] struct Srv { diff --git a/store/postgres/src/deployment.rs b/store/postgres/src/deployment.rs index 68435c536a2..b18737ecbc9 100644 --- a/store/postgres/src/deployment.rs +++ b/store/postgres/src/deployment.rs @@ -1010,3 +1010,31 @@ pub fn set_entity_count( .execute(conn)?; Ok(()) } + +pub fn set_earliest_block( + conn: &PgConnection, + site: &Site, + earliest_block: BlockNumber, +) -> Result<(), StoreError> { + use subgraph_deployment as d; + + update(d::table.filter(d::id.eq(site.id))) + .set(d::earliest_block_number.eq(earliest_block)) + .execute(conn)?; + Ok(()) +} + +/// Lock the row for `site` in `subgraph_deployment` for update. This lock +/// is used to coordinate the changes that the subgraph writer makes with +/// changes that other parts of the system, in particular, pruning make +// see also: deployment-lock-for-update +pub fn lock(conn: &PgConnection, site: &Site) -> Result<(), StoreError> { + use subgraph_deployment as d; + + d::table + .select(d::id) + .filter(d::id.eq(site.id)) + .for_update() + .get_result::(conn)?; + Ok(()) +} diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index 3ff624738c8..188b138df89 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -4,7 +4,7 @@ use diesel::pg::PgConnection; use diesel::prelude::*; use diesel::r2d2::{ConnectionManager, PooledConnection}; use graph::blockchain::block_stream::FirehoseCursor; -use graph::components::store::{EntityKey, EntityType, StoredDynamicDataSource}; +use graph::components::store::{EntityKey, EntityType, PruneReporter, StoredDynamicDataSource}; use graph::components::versions::VERSIONS; use graph::data::query::Trace; use graph::data::subgraph::{status, SPEC_VERSION_0_0_6}; @@ -794,6 +794,61 @@ impl DeploymentStore { }) .await } + + pub(crate) async fn prune( + self: &Arc, + mut reporter: Box, + site: Arc, + earliest_block: BlockNumber, + reorg_threshold: BlockNumber, + prune_ratio: f64, + ) -> Result, StoreError> { + let store = self.clone(); + self.with_conn(move |conn, cancel| { + let layout = store.layout(conn, site.clone())?; + cancel.check_cancel()?; + let state = deployment::state(&conn, site.deployment.clone())?; + + if state.latest_block.number <= reorg_threshold { + return Ok(reporter); + } + + if state.earliest_block_number > earliest_block { + return Err(constraint_violation!("earliest block can not move back from {} to {}", state.earliest_block_number, earliest_block).into()); + } + + let final_block = state.latest_block.number - reorg_threshold; + if final_block <= earliest_block { + return Err(constraint_violation!("the earliest block {} must be at least {} blocks before the current latest block {}", earliest_block, reorg_threshold, state.latest_block.number).into()); + } + + if let Some((_, graft)) = deployment::graft_point(conn, &site.deployment)? { + if graft.block_number() >= earliest_block { + return Err(constraint_violation!("the earliest block {} must be after the graft point {}", earliest_block, graft.block_number()).into()); + } + } + + cancel.check_cancel()?; + + conn.transaction(|| { + deployment::set_earliest_block(conn, site.as_ref(), earliest_block) + })?; + + cancel.check_cancel()?; + + layout.prune_by_copying( + &store.logger, + reporter.as_mut(), + conn, + earliest_block, + final_block, + prune_ratio, + cancel, + )?; + Ok(reporter) + }) + .await + } } /// Methods that back the trait `graph::components::Store`, but have small @@ -1016,6 +1071,10 @@ impl DeploymentStore { // Make the changes let layout = self.layout(&conn, site.clone())?; + + // see also: deployment-lock-for-update + deployment::lock(&conn, &site)?; + let section = stopwatch.start_section("apply_entity_modifications"); let count = self.apply_entity_modifications( &conn, @@ -1068,6 +1127,9 @@ impl DeploymentStore { firehose_cursor: &FirehoseCursor, ) -> Result { let event = conn.transaction(|| -> Result<_, StoreError> { + // see also: deployment-lock-for-update + deployment::lock(conn, &site)?; + // Don't revert past a graft point let info = self.subgraph_info_with_conn(conn, site.as_ref())?; if let Some(graft_block) = info.graft_block { diff --git a/store/postgres/src/relational.rs b/store/postgres/src/relational.rs index 141501490b4..95383d4d229 100644 --- a/store/postgres/src/relational.rs +++ b/store/postgres/src/relational.rs @@ -14,6 +14,8 @@ mod ddl_tests; #[cfg(test)] mod query_tests; +mod prune; + use diesel::{connection::SimpleConnection, Connection}; use diesel::{debug_query, OptionalExtension, PgConnection, RunQueryDsl}; use graph::cheap_clone::CheapClone; @@ -1243,6 +1245,22 @@ impl Table { Ok(table) } + /// Create a table that is like `self` except that its name in the + /// database is based on `namespace` and `name` + pub fn new_like(&self, namespace: &Namespace, name: &SqlName) -> Arc
{ + let other = Table { + object: self.object.clone(), + name: name.clone(), + qualified_name: SqlName::qualified_name(namespace, &name), + columns: self.columns.clone(), + is_account_like: self.is_account_like, + position: self.position, + immutable: self.immutable, + }; + + Arc::new(other) + } + /// Find the column `name` in this table. The name must be in snake case, /// i.e., use SQL conventions pub fn column(&self, name: &SqlName) -> Option<&Column> { diff --git a/store/postgres/src/relational/ddl.rs b/store/postgres/src/relational/ddl.rs index 82ea9176053..0a4a235823c 100644 --- a/store/postgres/src/relational/ddl.rs +++ b/store/postgres/src/relational/ddl.rs @@ -1,10 +1,13 @@ -use std::fmt::{self, Write}; +use std::{ + fmt::{self, Write}, + iter, +}; use graph::prelude::BLOCK_NUMBER_MAX; use crate::relational::{ - ColumnType, BLOCK_COLUMN, BLOCK_RANGE_COLUMN, BYTE_ARRAY_PREFIX_SIZE, STRING_PREFIX_SIZE, - VID_COLUMN, + Catalog, ColumnType, BLOCK_COLUMN, BLOCK_RANGE_COLUMN, BYTE_ARRAY_PREFIX_SIZE, + STRING_PREFIX_SIZE, VID_COLUMN, }; use super::{Column, Layout, SqlName, Table}; @@ -54,7 +57,23 @@ impl Layout { } impl Table { - fn create_table(&self, out: &mut String, layout: &Layout) -> fmt::Result { + /// Return an iterator over all the column names of this table + /// + // This needs to stay in sync with `create_table` + pub(crate) fn column_names(&self) -> impl Iterator { + let block_column = if self.immutable { + BLOCK_COLUMN + } else { + BLOCK_RANGE_COLUMN + }; + let data_cols = self.columns.iter().map(|col| col.name.as_str()); + iter::once(VID_COLUMN) + .chain(data_cols) + .chain(iter::once(block_column)) + } + + // Changes to this function require changing `column_names`, too + pub(crate) fn create_table(&self, out: &mut String, layout: &Layout) -> fmt::Result { fn columns_ddl(table: &Table) -> Result { let mut cols = String::new(); let mut first = true; @@ -109,12 +128,76 @@ impl Table { self.exclusion_ddl( out, layout.catalog.site.namespace.as_str(), - layout.catalog.create_exclusion_constraint(), + Catalog::create_exclusion_constraint(), ) } } - fn create_time_travel_indexes(&self, out: &mut String, layout: &Layout) -> fmt::Result { + /// Generate SQL that renames the table `from` to `to`. The code assumes + /// that `from` has been created with `create_table`, but that no other + /// indexes have been created on it, and also renames any indexes and + /// constraints, so that the database, after running the generated SQL, + /// will have the exact same table as if `from.create_sql` had been run + /// initially. + /// + /// The code does not do anything about the sequence for `vid`; it is + /// assumed that both `from` and `to` already use the same sequence for + /// that + /// + /// For mutable tables, if `has_exclusion_constraint` is true, assume + /// there is an exclusion constraint on `(id, block_range)`; if it is + /// false, assume there is a GIST index on those columns. For immutbale + /// tables, this argument has no effect. It is assumed that the name of + /// the constraint or index is what `create_table` uses for these. + pub(crate) fn rename_sql( + out: &mut String, + layout: &Layout, + from: &Table, + to: &Table, + has_exclusion_constraint: bool, + ) -> fmt::Result { + let nsp = layout.site.namespace.as_str(); + let from_name = &from.name; + let to_name = &to.name; + let id = &to.primary_key().name; + + writeln!( + out, + r#"alter table "{nsp}"."{from_name}" rename to "{to_name}";"# + )?; + + writeln!( + out, + r#"alter table "{nsp}"."{to_name}" rename constraint "{from_name}_pkey" to "{to_name}_pkey";"# + )?; + + if to.immutable { + writeln!( + out, + r#"alter table "{nsp}"."{to_name}" rename constraint "{from_name}_{id}_key" to "{to_name}_{id}_key";"# + )?; + } else { + if has_exclusion_constraint { + writeln!( + out, + r#"alter table "{nsp}"."{to_name}" rename constraint "{from_name}_{id}_{BLOCK_RANGE_COLUMN}_excl" to "{to_name}_{id}_{BLOCK_RANGE_COLUMN}_excl";"# + )?; + } else { + writeln!( + out, + r#"alter index "{nsp}"."{from_name}_{id}_{BLOCK_RANGE_COLUMN}_excl" rename to "{to_name}_{id}_{BLOCK_RANGE_COLUMN}_excl";"# + )?; + } + } + + Ok(()) + } + + pub(crate) fn create_time_travel_indexes( + &self, + out: &mut String, + layout: &Layout, + ) -> fmt::Result { if self.immutable { write!( out, @@ -168,7 +251,11 @@ impl Table { } } - fn create_attribute_indexes(&self, out: &mut String, layout: &Layout) -> fmt::Result { + pub(crate) fn create_attribute_indexes( + &self, + out: &mut String, + layout: &Layout, + ) -> fmt::Result { // Create indexes. Skip columns whose type is an array of enum, // since there is no good way to index them with Postgres 9.6. // Once we move to Postgres 11, we can enable that diff --git a/store/postgres/src/relational/ddl_tests.rs b/store/postgres/src/relational/ddl_tests.rs index fb88e9cffce..97bf271e457 100644 --- a/store/postgres/src/relational/ddl_tests.rs +++ b/store/postgres/src/relational/ddl_tests.rs @@ -173,6 +173,37 @@ fn can_copy_from() { ); } +#[test] +fn replace_sql() { + const REPLACE_BAND: &str = "\ + alter table \"sgd0815\".\"band_r$\" rename to \"band\";\n\ + alter table \"sgd0815\".\"band\" rename constraint \"band_r$_pkey\" to \"band_pkey\";\n\ + alter table \"sgd0815\".\"band\" rename constraint \"band_r$_id_block_range_excl\" to \"band_id_block_range_excl\";"; + + const REPLACE_SONG: &str = "\ + alter table \"sgd0815\".\"song_r$\" rename to \"song\";\n\ + alter table \"sgd0815\".\"song\" rename constraint \"song_r$_pkey\" to \"song_pkey\";\n\ + alter table \"sgd0815\".\"song\" rename constraint \"song_r$_id_key\" to \"song_id_key\";"; + + fn check(exp: &str, object: &str) { + let layout = test_layout(MUSIC_GQL); + let src = layout + .table_for_entity(&EntityType::new(object.to_string())) + .unwrap(); + let dst = src.new_like(&layout.site.namespace, &SqlName(format!("{}_r$", src.name))); + + let mut out = String::new(); + Table::rename_sql(&mut out, &layout, &dst, &src, true) + .expect("generating rename_sql works"); + assert_eq!(exp, out.trim()); + src.create_table(&mut out, &layout).unwrap(); + dst.create_table(&mut out, &layout).unwrap(); + } + + check(REPLACE_BAND, "Band"); + check(REPLACE_SONG, "Song"); +} + const THING_GQL: &str = " type Thing @entity { id: ID! diff --git a/store/postgres/src/relational/prune.rs b/store/postgres/src/relational/prune.rs new file mode 100644 index 00000000000..83b94a9ab84 --- /dev/null +++ b/store/postgres/src/relational/prune.rs @@ -0,0 +1,318 @@ +use std::{fmt::Write, sync::Arc, time::Instant}; + +use diesel::{ + connection::SimpleConnection, + sql_query, + sql_types::{BigInt, Integer, Nullable}, + Connection, PgConnection, RunQueryDsl, +}; +use graph::{ + components::store::PruneReporter, + prelude::{BlockNumber, CancelHandle, CancelToken, CancelableError, CheapClone, StoreError}, + slog::Logger, +}; +use itertools::Itertools; + +use crate::{ + catalog, + copy::AdaptiveBatchSize, + deployment, + relational::{Table, VID_COLUMN}, +}; + +use super::{Layout, SqlName}; + +/// Utility to copy relevant data out of a source table and into a new +/// destination table and replace the source table with the destination +/// table +struct TablePair { + src: Arc
, + dst: Arc
, +} + +impl TablePair { + /// Create a `TablePair` for `src`. This creates a new table `dst` with + /// the same structure as the `src` table in the database, but without + /// various indexes. Those are created with `switch` + fn create(conn: &PgConnection, layout: &Layout, src: Arc
) -> Result { + let new_name = SqlName::verbatim(format!("{}_n$", src.name)); + let nsp = &layout.site.namespace; + + let dst = src.new_like(&layout.site.namespace, &new_name); + + let mut query = String::new(); + if catalog::table_exists(conn, &layout.site.namespace, &dst.name)? { + writeln!(query, "truncate table {nsp}.{new_name};")?; + } else { + dst.create_table(&mut query, layout)?; + + // Have the new table use the same vid sequence as the source + // table + writeln!( + query, + "\ + alter table {nsp}.{new_name} \ + alter column {VID_COLUMN} \ + set default nextval('{nsp}.{src_name}_vid_seq'::regclass);", + src_name = src.name + )?; + writeln!(query, "drop sequence {nsp}.{new_name}_vid_seq;")?; + writeln!( + query, + "alter sequence {nsp}.{src_name}_vid_seq owned by {nsp}.{new_name}.vid", + src_name = src.name + )?; + } + conn.batch_execute(&query)?; + + Ok(TablePair { src, dst }) + } + + fn copy_final_entities( + &self, + conn: &PgConnection, + reporter: &mut dyn PruneReporter, + earliest_block: BlockNumber, + final_block: BlockNumber, + cancel: &CancelHandle, + ) -> Result> { + #[derive(QueryableByName)] + struct VidRange { + #[sql_type = "Nullable"] + min_vid: Option, + #[sql_type = "Nullable"] + max_vid: Option, + } + + #[derive(QueryableByName)] + struct LastVid { + #[sql_type = "BigInt"] + rows: i64, + #[sql_type = "BigInt"] + last_vid: i64, + } + + let (min_vid, max_vid) = match sql_query(&format!( + "select min(vid) as min_vid, max(vid) as max_vid from {src} \ + where coalesce(upper(block_range), 2147483647) > $1 \ + and coalesce(upper(block_range), 2147483647) <= $2", + src = self.src.qualified_name + )) + .bind::(earliest_block) + .bind::(final_block) + .get_result::(conn)? + { + VidRange { + min_vid: None, + max_vid: None, + } => { + return Ok(0); + } + VidRange { + min_vid: Some(min), + max_vid: Some(max), + } => (min, max), + _ => unreachable!("min and max are Some or None at the same time"), + }; + cancel.check_cancel()?; + + let column_list = self.column_list(); + + let mut batch_size = AdaptiveBatchSize::new(&self.src); + let mut next_vid = min_vid; + let mut total_rows: usize = 0; + loop { + let start = Instant::now(); + let LastVid { last_vid, rows } = conn.transaction(|| { + sql_query(&format!( + "with cp as (insert into {dst}({column_list}) \ + select {column_list} from {src} \ + where lower(block_range) <= $2 \ + and coalesce(upper(block_range), 2147483647) > $1 \ + and coalesce(upper(block_range), 2147483647) <= $2 \ + and vid >= $3 \ + and vid <= $4 \ + order by vid \ + limit $5 \ + returning vid) \ + select max(cp.vid) as last_vid, count(*) as rows from cp", + src = self.src.qualified_name, + dst = self.dst.qualified_name + )) + .bind::(earliest_block) + .bind::(final_block) + .bind::(next_vid) + .bind::(max_vid) + .bind::(&batch_size) + .get_result::(conn) + })?; + cancel.check_cancel()?; + + total_rows += rows as usize; + reporter.copy_final_batch( + self.src.name.as_str(), + rows as usize, + total_rows, + last_vid >= max_vid, + ); + + if last_vid >= max_vid { + break; + } + + batch_size.adapt(start.elapsed()); + next_vid = last_vid + 1; + } + + Ok(total_rows) + } + + fn copy_nonfinal_entities( + &self, + conn: &PgConnection, + final_block: BlockNumber, + ) -> Result { + let column_list = self.column_list(); + + sql_query(&format!( + "insert into {dst}({column_list}) \ + select {column_list} from {src} \ + where coalesce(upper(block_range), 2147483647) > $1 \ + and block_range && int4range($1, null) \ + order by vid", + dst = self.dst.qualified_name, + src = self.src.qualified_name, + )) + .bind::(final_block) + .execute(conn) + .map_err(StoreError::from) + } + + /// Replace the `src` table with the `dst` table. This makes sure (as + /// does the rest of the code in `TablePair`) that the table and all + /// associated objects (indexes, constraints, etc.) have the same names + /// as they had initially so that pruning can be performed again in the + /// future without any name clashes in the database. + fn switch(self, conn: &PgConnection, layout: &Layout) -> Result<(), StoreError> { + sql_query(&format!("drop table {}", self.src.qualified_name)).execute(conn)?; + + let uses_excl = + catalog::has_exclusion_constraint(conn, &layout.site.namespace, &self.dst.name)?; + let mut query = String::new(); + Table::rename_sql(&mut query, &layout, &self.dst, &self.src, uses_excl)?; + self.src.create_time_travel_indexes(&mut query, layout)?; + self.src.create_attribute_indexes(&mut query, layout)?; + + conn.batch_execute(&query)?; + + Ok(()) + } + + fn column_list(&self) -> String { + self.src + .column_names() + .map(|name| format!("\"{name}\"")) + .join(", ") + } +} + +impl Layout { + /// Remove all data from the underlying deployment that is not needed to + /// respond to queries before block `earliest_block`. The strategy + /// implemented here works well for situations in which pruning will + /// remove a large amount of data from the subgraph (at least 50%) + /// + /// Blocks before `final_block` are considered final and it is assumed + /// that they will not be modified in any way while pruning is running. + /// Only tables where the ratio of entities to entity versions is below + /// `prune_ratio` will actually be pruned. + pub fn prune_by_copying( + &self, + _logger: &Logger, + reporter: &mut dyn PruneReporter, + conn: &PgConnection, + earliest_block: BlockNumber, + final_block: BlockNumber, + prune_ratio: f64, + cancel: &CancelHandle, + ) -> Result<(), CancelableError> { + // Analyze all tables and get statistics for them + let mut tables: Vec<_> = self.tables.values().collect(); + tables.sort_by_key(|table| table.name.as_str()); + for table in tables { + reporter.start_analyze(table.name.as_str()); + table.analyze(conn)?; + reporter.finish_analyze(table.name.as_str()); + cancel.check_cancel()?; + } + let stats = catalog::stats(conn, &self.site.namespace)?; + + // Determine which tables are prunable and create a shadow table for + // them via `TablePair::create` + let prunable_tables = { + let mut prunable_tables: Vec = self + .tables + .values() + .filter_map(|table| { + stats + .iter() + .find(|s| s.tablename == table.name.as_str()) + .map(|s| (table, s)) + }) + .filter(|(_, stats)| stats.ratio <= prune_ratio) + .map(|(table, _)| TablePair::create(conn, self, table.cheap_clone())) + .collect::>()?; + prunable_tables.sort_by(|a, b| a.src.name.as_str().cmp(b.src.name.as_str())); + prunable_tables + }; + cancel.check_cancel()?; + + // Copy final entities. This can happen in parallel to indexing as + // that part of the table will not change + reporter.copy_final_start(earliest_block, final_block); + for table in &prunable_tables { + table.copy_final_entities(conn, reporter, earliest_block, final_block, cancel)?; + } + reporter.copy_final_finish(); + + let prunable_src: Vec<_> = prunable_tables + .iter() + .map(|table| table.src.clone()) + .collect(); + + // Copy nonfinal entities, and replace the original `src` table with + // the smaller `dst` table + reporter.start_switch(); + conn.transaction(|| -> Result<(), CancelableError> { + // see also: deployment-lock-for-update + deployment::lock(conn, &self.site)?; + + for table in &prunable_tables { + reporter.copy_nonfinal_start(table.src.name.as_str()); + let rows = table.copy_nonfinal_entities(conn, final_block)?; + reporter.copy_nonfinal_finish(table.src.name.as_str(), rows); + cancel.check_cancel()?; + } + + for table in prunable_tables { + table.switch(conn, self)?; + cancel.check_cancel()?; + } + + Ok(()) + })?; + reporter.finish_switch(); + + // Analyze the new tables + for table in prunable_src { + reporter.start_analyze(table.name.as_str()); + table.analyze(conn)?; + reporter.finish_analyze(table.name.as_str()); + cancel.check_cancel()?; + } + + reporter.finish_prune(); + + Ok(()) + } +} diff --git a/store/postgres/src/subgraph_store.rs b/store/postgres/src/subgraph_store.rs index 23ee0358d8f..19bc747f298 100644 --- a/store/postgres/src/subgraph_store.rs +++ b/store/postgres/src/subgraph_store.rs @@ -17,7 +17,7 @@ use graph::{ server::index_node::VersionInfo, store::{ self, BlockStore, DeploymentLocator, DeploymentSchemaVersion, - EnsLookup as EnsLookupTrait, SubgraphFork, + EnsLookup as EnsLookupTrait, PruneReporter, SubgraphFork, }, }, constraint_violation, @@ -1054,6 +1054,44 @@ impl SubgraphStoreInner { let (store, site) = self.store(&deployment.hash)?; store.set_account_like(site, table, is_account_like).await } + + /// Remove the history that is only needed to respond to queries before + /// block number `earliest_block` from the given deployment + /// + /// Only tables with a ratio of entities to entity versions below + /// `prune_ratio` will be pruned; that ratio is determined by looking at + /// Postgres planner stats to avoid lengthy counting queries. It is + /// assumed that if the ratio is higher than `prune_ratio` that pruning + /// won't make much of a difference and will just cause unnecessary + /// work. + /// + /// The `reorg_threshold` is used to determine which blocks will not be + /// modified any more by the subgraph writer that may be running + /// concurrently to reduce the amount of time that the writer needs to + /// be locked out while pruning is happening. + /// + /// Pruning can take a long time, and is structured into multiple + /// transactions such that none of them takes an excessively long time. + /// If pruning gets interrupted, it may leave some intermediate tables + /// in the database behind. Those will get cleaned up the next time an + /// attempt is made to prune the same deployment. + pub async fn prune( + &self, + reporter: Box, + deployment: &DeploymentLocator, + earliest_block: BlockNumber, + reorg_threshold: BlockNumber, + prune_ratio: f64, + ) -> Result, StoreError> { + // Find the store by the deployment id; otherwise, we could only + // prune the active copy of the deployment with `deployment.hash` + let site = self.find_site(deployment.id.into())?; + let store = self.for_site(&site)?; + + store + .prune(reporter, site, earliest_block, reorg_threshold, prune_ratio) + .await + } } struct EnsLookup { diff --git a/store/postgres/tests/graft.rs b/store/postgres/tests/graft.rs index b7d51b3dfda..1ed2b259c7d 100644 --- a/store/postgres/tests/graft.rs +++ b/store/postgres/tests/graft.rs @@ -1,11 +1,10 @@ use graph::blockchain::block_stream::FirehoseCursor; -use hex_literal::hex; use lazy_static::lazy_static; use std::{marker::PhantomData, str::FromStr}; use test_store::*; use graph::components::store::{ - DeploymentLocator, EntityKey, EntityOrder, EntityQuery, EntityType, + DeploymentLocator, EntityKey, EntityOrder, EntityQuery, EntityType, PruneReporter, }; use graph::data::store::scalar; use graph::data::subgraph::schema::*; @@ -13,7 +12,6 @@ use graph::data::subgraph::*; use graph::prelude::*; use graph::semver::Version; use graph_store_postgres::SubgraphStore as DieselSubgraphStore; -use web3::types::H256; const USER_GQL: &str = " enum Color { yellow, red, blue, green } @@ -74,30 +72,24 @@ type User @entity(immutable: true) { const USER: &str = "User"; -macro_rules! block_pointer { - ($hash:expr, $number:expr) => {{ - BlockPtr::from((H256::from(hex!($hash)), $number as u64)) - }}; -} - lazy_static! { static ref TEST_SUBGRAPH_ID: DeploymentHash = DeploymentHash::new("testsubgraph").unwrap(); static ref TEST_SUBGRAPH_SCHEMA: Schema = Schema::parse(USER_GQL, TEST_SUBGRAPH_ID.clone()).expect("Failed to parse user schema"); static ref BLOCKS: Vec = vec![ - block_pointer!( - "bd34884280958002c51d3f7b5f853e6febeba33de0f40d15b0363006533c924f", - 0 - ), - block_pointer!( - "8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13", - 1 - ), - block_pointer!( - "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1", - 2 - ), - ]; + "bd34884280958002c51d3f7b5f853e6febeba33de0f40d15b0363006533c924f", + "8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13", + "b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1", + "7347afe69254df06729e123610b00b8b11f15cfae3241f9366fb113aec07489c", + "f8ccbd3877eb98c958614f395dd351211afb9abba187bfc1fb4ac414b099c4a6", + "7b0ea919e258eb2b119eb32de56b85d12d50ac6a9f7c5909f843d6172c8ba196", + "6b834521bb753c132fdcf0e1034803ed9068e324112f8750ba93580b393a986b", + "7cce080f5a49c2997a6cc65fc1cee9910fd8fc3721b7010c0b5d0873e2ac785e" + ] + .iter() + .enumerate() + .map(|(idx, hash)| BlockPtr::try_from((*hash, idx as i64)).unwrap()) + .collect(); } /// Test harness for running database integration tests. @@ -115,6 +107,8 @@ where // Seed database with test data let deployment = insert_test_data(store.clone()).await; + flush(&deployment).await.unwrap(); + // Run test test(store.cheap_clone(), deployment.clone()) .await @@ -449,3 +443,99 @@ fn copy() { check_graft(store, deployment).await }) } + +#[test] +fn prune() { + fn check_at_block( + store: &DieselSubgraphStore, + src: &DeploymentLocator, + block: BlockNumber, + exp: Vec<&str>, + ) { + let query = EntityQuery::new( + src.hash.clone(), + block, + EntityCollection::All(vec![( + EntityType::new("User".to_string()), + AttributeNames::All, + )]), + ); + + let act: Vec<_> = store + .find(query) + .unwrap() + .into_iter() + .map(|entity| entity.id().unwrap()) + .collect(); + assert_eq!(act, exp); + } + + async fn prune( + store: &DieselSubgraphStore, + src: &DeploymentLocator, + earliest_block: BlockNumber, + ) -> Result<(), StoreError> { + struct Progress; + impl PruneReporter for Progress {} + let reporter = Box::new(Progress); + + store + .prune(reporter, &src, earliest_block, 1, 1.1) + .await + .map(|_| ()) + } + + run_test(|store, src| async move { + // The setup sets the subgraph pointer to block 2, we try to set + // earliest block to 5 + prune(&store, &src, 5) + .await + .expect_err("setting earliest block later than latest does not work"); + + // Latest block 2 minus reorg threshold 1 means we need to copy + // final blocks from block 1, but want earliest as block 2, i.e. no + // final blocks which won't work + prune(&store, &src, 2) + .await + .expect_err("setting earliest block after last final block fails"); + + // Add another version for user 2 at block 4 + let user2 = create_test_entity( + "2", + USER, + "Cindini", + "dinici@email.com", + 44 as i32, + 157.1, + true, + Some("red"), + ); + transact_and_wait(&store, &src, BLOCKS[5].clone(), vec![user2]) + .await + .unwrap(); + + // Setup and the above addition create these user versions: + // id | versions + // ---+--------- + // 1 | [0,) + // 2 | [1,5) [5,) + // 3 | [1,2) [2,) + + // Forward block ptr to block 5 + transact_and_wait(&store, &src, BLOCKS[6].clone(), vec![]) + .await + .unwrap(); + // Pruning only removes the [1,2) version of user 3 + prune(&store, &src, 3).await.expect("pruning works"); + + // Check which versions exist at every block, even if they are + // before the new earliest block, since we don't have a convenient + // way to load all entity versions with their block range + check_at_block(&store, &src, 0, vec!["1"]); + check_at_block(&store, &src, 1, vec!["1", "2"]); + for block in 2..=5 { + check_at_block(&store, &src, block, vec!["1", "2", "3"]); + } + Ok(()) + }) +} From b3d5c33375106bfae099333c371098c1ea1d7b52 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 31 Aug 2022 10:01:35 -0700 Subject: [PATCH 012/253] node: Expose SubgraphStore.prune through `graphman prune` Fixes https://github.com/graphprotocol/graph-node/issues/3665 --- node/src/bin/manager.rs | 20 ++++ node/src/manager/commands/mod.rs | 1 + node/src/manager/commands/prune.rs | 160 +++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 node/src/manager/commands/prune.rs diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index ce0cd84f3dc..1e8da9dd8e2 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -209,6 +209,18 @@ pub enum Command { /// Manage database indexes Index(IndexCommand), + + /// Prune deployments + Prune { + /// The deployment to prune (see `help info`) + deployment: DeploymentSearch, + /// Prune tables with a ratio of entities to entity versions lower than this + #[structopt(long, short, default_value = "0.20")] + prune_ratio: f64, + /// How much history to keep in blocks + #[structopt(long, short, default_value = "10000")] + history: usize, + }, } impl Command { @@ -1034,6 +1046,14 @@ async fn main() -> anyhow::Result<()> { } } } + Prune { + deployment, + history, + prune_ratio, + } => { + let (store, primary_pool) = ctx.store_and_primary(); + commands::prune::run(store, primary_pool, deployment, history, prune_ratio).await + } } } diff --git a/node/src/manager/commands/mod.rs b/node/src/manager/commands/mod.rs index c3ae31bbb79..eab1f1a0f7a 100644 --- a/node/src/manager/commands/mod.rs +++ b/node/src/manager/commands/mod.rs @@ -7,6 +7,7 @@ pub mod create; pub mod index; pub mod info; pub mod listen; +pub mod prune; pub mod query; pub mod remove; pub mod rewind; diff --git a/node/src/manager/commands/prune.rs b/node/src/manager/commands/prune.rs new file mode 100644 index 00000000000..80b94fbb4a5 --- /dev/null +++ b/node/src/manager/commands/prune.rs @@ -0,0 +1,160 @@ +use std::{io::Write, sync::Arc, time::Instant}; + +use graph::{ + components::store::{PruneReporter, StatusStore}, + data::subgraph::status, + prelude::{anyhow, BlockNumber}, +}; +use graph_chain_ethereum::ENV_VARS as ETH_ENV; +use graph_store_postgres::{connection_pool::ConnectionPool, Store}; + +use crate::manager::deployment::DeploymentSearch; + +struct Progress { + start: Instant, + analyze_start: Instant, + switch_start: Instant, + final_start: Instant, + final_table_start: Instant, + nonfinal_start: Instant, +} + +impl Progress { + fn new() -> Self { + Self { + start: Instant::now(), + analyze_start: Instant::now(), + switch_start: Instant::now(), + final_start: Instant::now(), + final_table_start: Instant::now(), + nonfinal_start: Instant::now(), + } + } +} + +impl PruneReporter for Progress { + fn start_analyze(&mut self, table: &str) { + print!("analyze {table:48} "); + std::io::stdout().flush().ok(); + self.analyze_start = Instant::now(); + } + + fn finish_analyze(&mut self, table: &str) { + println!( + "\ranalyze {table:48} (done in {}s)", + self.analyze_start.elapsed().as_secs() + ); + std::io::stdout().flush().ok(); + } + + fn copy_final_start(&mut self, earliest_block: BlockNumber, final_block: BlockNumber) { + println!("copy final entities (versions live between {earliest_block} and {final_block})"); + self.final_start = Instant::now(); + self.final_table_start = self.final_start; + } + + fn copy_final_batch(&mut self, table: &str, _rows: usize, total_rows: usize, finished: bool) { + if finished { + println!( + "\r copy final {table:43} ({total_rows} rows in {}s)", + self.final_table_start.elapsed().as_secs() + ); + self.final_table_start = Instant::now(); + } else { + print!( + "\r copy final {table:43} ({total_rows} rows in {}s)", + self.final_table_start.elapsed().as_secs() + ); + } + std::io::stdout().flush().ok(); + } + + fn copy_final_finish(&mut self) { + println!( + "finished copying final entity versions in {}s", + self.final_start.elapsed().as_secs() + ); + } + + fn start_switch(&mut self) { + println!("blocking writes and switching tables"); + self.switch_start = Instant::now(); + } + + fn finish_switch(&mut self) { + println!( + "enabling writes. Switching took {}s", + self.switch_start.elapsed().as_secs() + ); + } + + fn copy_nonfinal_start(&mut self, table: &str) { + print!(" copy nonfinal {table:40}"); + std::io::stdout().flush().ok(); + self.nonfinal_start = Instant::now(); + } + + fn copy_nonfinal_finish(&mut self, table: &str, rows: usize) { + println!( + "\r copy nonfinal {table:40} ({rows} rows in {}s)", + self.nonfinal_start.elapsed().as_secs() + ); + std::io::stdout().flush().ok(); + } + + fn finish_prune(&mut self) { + println!("finished pruning in {}s", self.start.elapsed().as_secs()); + } +} + +pub async fn run( + store: Arc, + primary_pool: ConnectionPool, + search: DeploymentSearch, + history: usize, + prune_ratio: f64, +) -> Result<(), anyhow::Error> { + let history = history as BlockNumber; + let deployment = search.locate_unique(&primary_pool)?; + let mut info = store + .status(status::Filter::DeploymentIds(vec![deployment.id]))? + .pop() + .ok_or_else(|| anyhow!("deployment {deployment} not found"))?; + if info.chains.len() > 1 { + return Err(anyhow!( + "deployment {deployment} indexes {} chains, not sure how to deal with more than one chain", + info.chains.len() + )); + } + let status = info + .chains + .pop() + .ok_or_else(|| anyhow!("deployment {} does not index any chain", deployment))?; + let latest = status.latest_block.map(|ptr| ptr.number()).unwrap_or(0); + if latest <= history { + return Err(anyhow!("deployment {deployment} has only indexed up to block {latest} and we can't preserve {history} blocks of history")); + } + + println!("prune {deployment}"); + println!(" latest: {latest}"); + println!(" final: {}", latest - ETH_ENV.reorg_threshold); + println!(" earliest: {}", latest - history); + + let reporter = Box::new(Progress::new()); + store + .subgraph_store() + .prune( + reporter, + &deployment, + latest - history, + // Using the setting for eth chains is a bit lazy; the value + // should really depend on the chain, but we don't have a + // convenient way to figure out how each chain deals with + // finality + ETH_ENV.reorg_threshold, + prune_ratio, + ) + .await?; + + Ok(()) +} From 7d2b5e2ed317bf1eb2d94e3f5f6e7083e34454d5 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 2 Sep 2022 15:52:56 -0700 Subject: [PATCH 013/253] graph, node, store: Improve output from 'graphman prune' --- graph/src/components/store/mod.rs | 17 +++++- node/src/manager/commands/prune.rs | 81 +++++++++++++++++--------- node/src/manager/commands/stats.rs | 50 +++++++++++----- store/postgres/src/catalog.rs | 48 +++++++++------ store/postgres/src/lib.rs | 2 +- store/postgres/src/relational/prune.rs | 23 ++++++-- 6 files changed, 153 insertions(+), 68 deletions(-) diff --git a/graph/src/components/store/mod.rs b/graph/src/components/store/mod.rs index 88fb1d65f10..77cb3635e94 100644 --- a/graph/src/components/store/mod.rs +++ b/graph/src/components/store/mod.rs @@ -1077,12 +1077,25 @@ impl ReadStore for EmptyStore { } } +/// An estimate of the number of entities and the number of entity versions +/// in a database table +#[derive(Clone, Debug)] +pub struct VersionStats { + pub entities: i32, + pub versions: i32, + pub tablename: String, + /// The ratio `entities / versions` + pub ratio: f64, +} + /// Callbacks for `SubgraphStore.prune` so that callers can report progress /// of the pruning procedure to users #[allow(unused_variables)] pub trait PruneReporter: Send + 'static { - fn start_analyze(&mut self, table: &str) {} - fn finish_analyze(&mut self, table: &str) {} + fn start_analyze(&mut self) {} + fn start_analyze_table(&mut self, table: &str) {} + fn finish_analyze_table(&mut self, table: &str) {} + fn finish_analyze(&mut self, stats: &[VersionStats]) {} fn copy_final_start(&mut self, earliest_block: BlockNumber, final_block: BlockNumber) {} fn copy_final_batch(&mut self, table: &str, rows: usize, total_rows: usize, finished: bool) {} diff --git a/node/src/manager/commands/prune.rs b/node/src/manager/commands/prune.rs index 80b94fbb4a5..e635df7775e 100644 --- a/node/src/manager/commands/prune.rs +++ b/node/src/manager/commands/prune.rs @@ -1,4 +1,9 @@ -use std::{io::Write, sync::Arc, time::Instant}; +use std::{ + collections::HashSet, + io::Write, + sync::Arc, + time::{Duration, Instant}, +}; use graph::{ components::store::{PruneReporter, StatusStore}, @@ -8,7 +13,10 @@ use graph::{ use graph_chain_ethereum::ENV_VARS as ETH_ENV; use graph_store_postgres::{connection_pool::ConnectionPool, Store}; -use crate::manager::deployment::DeploymentSearch; +use crate::manager::{ + commands::stats::{abbreviate_table_name, show_stats}, + deployment::DeploymentSearch, +}; struct Progress { start: Instant, @@ -32,78 +40,93 @@ impl Progress { } } +fn print_copy_header() { + println!("{:^30} | {:^10} | {:^11}", "table", "versions", "time"); + println!("{:-^30}-+-{:-^10}-+-{:-^11}", "", "", ""); + std::io::stdout().flush().ok(); +} + +fn print_copy_row(table: &str, total_rows: usize, elapsed: Duration) { + print!( + "\r{:<30} | {:>10} | {:>9}s", + abbreviate_table_name(table, 30), + total_rows, + elapsed.as_secs() + ); + std::io::stdout().flush().ok(); +} + impl PruneReporter for Progress { - fn start_analyze(&mut self, table: &str) { - print!("analyze {table:48} "); - std::io::stdout().flush().ok(); + fn start_analyze(&mut self) { + print!("Analyze tables"); self.analyze_start = Instant::now(); } - fn finish_analyze(&mut self, table: &str) { + fn start_analyze_table(&mut self, table: &str) { + print!("\rAnalyze {table:48} "); + std::io::stdout().flush().ok(); + } + + fn finish_analyze(&mut self, stats: &[graph::components::store::VersionStats]) { println!( - "\ranalyze {table:48} (done in {}s)", + "\rAnalyzed {} tables in {}s", + stats.len(), self.analyze_start.elapsed().as_secs() ); - std::io::stdout().flush().ok(); + show_stats(stats, HashSet::new()).ok(); + println!(""); } fn copy_final_start(&mut self, earliest_block: BlockNumber, final_block: BlockNumber) { - println!("copy final entities (versions live between {earliest_block} and {final_block})"); + println!("Copy final entities (versions live between {earliest_block} and {final_block})"); + print_copy_header(); + self.final_start = Instant::now(); self.final_table_start = self.final_start; } fn copy_final_batch(&mut self, table: &str, _rows: usize, total_rows: usize, finished: bool) { + print_copy_row(table, total_rows, self.final_table_start.elapsed()); if finished { - println!( - "\r copy final {table:43} ({total_rows} rows in {}s)", - self.final_table_start.elapsed().as_secs() - ); + println!(""); self.final_table_start = Instant::now(); - } else { - print!( - "\r copy final {table:43} ({total_rows} rows in {}s)", - self.final_table_start.elapsed().as_secs() - ); } std::io::stdout().flush().ok(); } fn copy_final_finish(&mut self) { println!( - "finished copying final entity versions in {}s", + "Finished copying final entity versions in {}s\n", self.final_start.elapsed().as_secs() ); } fn start_switch(&mut self) { - println!("blocking writes and switching tables"); + println!("Blocking writes and switching tables"); + print_copy_header(); self.switch_start = Instant::now(); } fn finish_switch(&mut self) { println!( - "enabling writes. Switching took {}s", + "Enabling writes. Switching took {}s\n", self.switch_start.elapsed().as_secs() ); } fn copy_nonfinal_start(&mut self, table: &str) { - print!(" copy nonfinal {table:40}"); - std::io::stdout().flush().ok(); + print_copy_row(table, 0, Duration::from_secs(0)); self.nonfinal_start = Instant::now(); } fn copy_nonfinal_finish(&mut self, table: &str, rows: usize) { - println!( - "\r copy nonfinal {table:40} ({rows} rows in {}s)", - self.nonfinal_start.elapsed().as_secs() - ); + print_copy_row(table, rows, self.nonfinal_start.elapsed()); + println!(""); std::io::stdout().flush().ok(); } fn finish_prune(&mut self) { - println!("finished pruning in {}s", self.start.elapsed().as_secs()); + println!("Finished pruning in {}s", self.start.elapsed().as_secs()); } } @@ -138,7 +161,7 @@ pub async fn run( println!("prune {deployment}"); println!(" latest: {latest}"); println!(" final: {}", latest - ETH_ENV.reorg_threshold); - println!(" earliest: {}", latest - history); + println!(" earliest: {}\n", latest - history); let reporter = Box::new(Progress::new()); store diff --git a/node/src/manager/commands/stats.rs b/node/src/manager/commands/stats.rs index 8e382b40d6f..ca07cb110dc 100644 --- a/node/src/manager/commands/stats.rs +++ b/node/src/manager/commands/stats.rs @@ -1,10 +1,12 @@ use std::collections::HashMap; +use std::collections::HashSet; use std::sync::Arc; use crate::manager::deployment::DeploymentSearch; use diesel::r2d2::ConnectionManager; use diesel::r2d2::PooledConnection; use diesel::PgConnection; +use graph::components::store::VersionStats; use graph::prelude::anyhow; use graph_store_postgres::command_support::catalog as store_catalog; use graph_store_postgres::command_support::catalog::Site; @@ -48,12 +50,23 @@ pub async fn account_like( Ok(()) } -pub fn show( - pools: HashMap, - search: &DeploymentSearch, -) -> Result<(), anyhow::Error> { - let (site, conn) = site_and_conn(pools, search)?; +pub fn abbreviate_table_name(table: &str, size: usize) -> String { + if table.len() > size { + let fragment = size / 2 - 2; + let last = table.len() - fragment; + let mut table = table.to_string(); + table.replace_range(fragment..last, ".."); + let table = table.trim().to_string(); + table + } else { + table.to_string() + } +} +pub fn show_stats( + stats: &[VersionStats], + account_like: HashSet, +) -> Result<(), anyhow::Error> { fn header() { println!( "{:^30} | {:^10} | {:^10} | {:^7}", @@ -66,10 +79,10 @@ pub fn show( println!(" (a): account-like flag set"); } - fn print_stats(s: &store_catalog::VersionStats, account_like: bool) { + fn print_stats(s: &VersionStats, account_like: bool) { println!( "{:<26} {:3} | {:>10} | {:>10} | {:>5.1}%", - s.tablename, + abbreviate_table_name(&s.tablename, 26), if account_like { "(a)" } else { " " }, s.entities, s.versions, @@ -77,19 +90,30 @@ pub fn show( ); } - let stats = store_catalog::stats(&conn, &site.namespace)?; - - let account_like = store_catalog::account_like(&conn, &site)?; - header(); - for s in &stats { + for s in stats { print_stats(s, account_like.contains(&s.tablename)); } - footer(); + if !account_like.is_empty() { + footer(); + } Ok(()) } +pub fn show( + pools: HashMap, + search: &DeploymentSearch, +) -> Result<(), anyhow::Error> { + let (site, conn) = site_and_conn(pools, search)?; + + let stats = store_catalog::stats(&conn, &site.namespace)?; + + let account_like = store_catalog::account_like(&conn, &site)?; + + show_stats(stats.as_slice(), account_like) +} + pub fn analyze( store: Arc, pool: ConnectionPool, diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index dd08d53ffc4..22bf3d941ef 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -6,6 +6,7 @@ use diesel::{ sql_types::{Array, Double, Nullable, Text}, ExpressionMethods, QueryDsl, }; +use graph::components::store::VersionStats; use std::collections::{HashMap, HashSet}; use std::fmt::Write; use std::iter::FromIterator; @@ -559,22 +560,31 @@ pub(crate) fn drop_index( Ok(()) } -/// An estimate of the number of entities and the number of entity versions -/// in a database table -#[derive(Clone, Debug, Queryable, QueryableByName)] -pub struct VersionStats { - #[sql_type = "Integer"] - pub entities: i32, - #[sql_type = "Integer"] - pub versions: i32, - #[sql_type = "Text"] - pub tablename: String, - /// The ratio `entities / versions` - #[sql_type = "Double"] - pub ratio: f64, -} - pub fn stats(conn: &PgConnection, namespace: &Namespace) -> Result, StoreError> { + #[derive(Queryable, QueryableByName)] + pub struct DbStats { + #[sql_type = "Integer"] + pub entities: i32, + #[sql_type = "Integer"] + pub versions: i32, + #[sql_type = "Text"] + pub tablename: String, + /// The ratio `entities / versions` + #[sql_type = "Double"] + pub ratio: f64, + } + + impl From for VersionStats { + fn from(s: DbStats) -> Self { + VersionStats { + entities: s.entities, + versions: s.versions, + tablename: s.tablename, + ratio: s.ratio, + } + } + } + // Get an estimate of number of rows (pg_class.reltuples) and number of // distinct entities (based on the planners idea of how many distinct // values there are in the `id` column) See the [Postgres @@ -599,8 +609,10 @@ pub fn stats(conn: &PgConnection, namespace: &Namespace) -> Result(namespace.as_str()) - .load::(conn) - .map_err(StoreError::from) + .load::(conn) + .map_err(StoreError::from)?; + + Ok(stats.into_iter().map(|s| s.into()).collect()) } diff --git a/store/postgres/src/lib.rs b/store/postgres/src/lib.rs index 88c15fca796..66211f96622 100644 --- a/store/postgres/src/lib.rs +++ b/store/postgres/src/lib.rs @@ -69,7 +69,7 @@ pub use self::subgraph_store::{unused, DeploymentPlacer, Shard, SubgraphStore, P pub mod command_support { pub mod catalog { pub use crate::block_store::primary as block_store; - pub use crate::catalog::{account_like, stats, VersionStats}; + pub use crate::catalog::{account_like, stats}; pub use crate::copy::{copy_state, copy_table_state}; pub use crate::primary::Connection; pub use crate::primary::{ diff --git a/store/postgres/src/relational/prune.rs b/store/postgres/src/relational/prune.rs index 83b94a9ab84..c942ff06760 100644 --- a/store/postgres/src/relational/prune.rs +++ b/store/postgres/src/relational/prune.rs @@ -238,14 +238,16 @@ impl Layout { ) -> Result<(), CancelableError> { // Analyze all tables and get statistics for them let mut tables: Vec<_> = self.tables.values().collect(); + reporter.start_analyze(); tables.sort_by_key(|table| table.name.as_str()); for table in tables { - reporter.start_analyze(table.name.as_str()); + reporter.start_analyze_table(table.name.as_str()); table.analyze(conn)?; - reporter.finish_analyze(table.name.as_str()); + reporter.finish_analyze_table(table.name.as_str()); cancel.check_cancel()?; } let stats = catalog::stats(conn, &self.site.namespace)?; + reporter.finish_analyze(stats.as_slice()); // Determine which tables are prunable and create a shadow table for // them via `TablePair::create` @@ -304,12 +306,23 @@ impl Layout { reporter.finish_switch(); // Analyze the new tables - for table in prunable_src { - reporter.start_analyze(table.name.as_str()); + reporter.start_analyze(); + for table in &prunable_src { + reporter.start_analyze_table(table.name.as_str()); table.analyze(conn)?; - reporter.finish_analyze(table.name.as_str()); + reporter.finish_analyze_table(table.name.as_str()); cancel.check_cancel()?; } + let stats: Vec<_> = catalog::stats(conn, &self.site.namespace)? + .into_iter() + .filter(|s| { + prunable_src + .iter() + .find(|table| table.name.as_str() == s.tablename) + .is_some() + }) + .collect(); + reporter.finish_analyze(stats.as_slice()); reporter.finish_prune(); From 3253d4d1248d3e06271237a6d500ee1abf5065d6 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 16 Sep 2022 09:40:31 -0700 Subject: [PATCH 014/253] store: Improve comments for the prune code Also, avoid a division by zero in an edge case --- store/postgres/src/copy.rs | 4 +++- store/postgres/src/deployment.rs | 7 ++++--- store/postgres/src/relational/prune.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/store/postgres/src/copy.rs b/store/postgres/src/copy.rs index 7b0e5efdc43..6c69664acfb 100644 --- a/store/postgres/src/copy.rs +++ b/store/postgres/src/copy.rs @@ -296,8 +296,10 @@ impl AdaptiveBatchSize { // get close to TARGET_DURATION for the time it takes to copy one // batch, but don't step up batch_size by more than 2x at once pub fn adapt(&mut self, duration: Duration) { + // Avoid division by zero + let duration = duration.as_millis().max(1); let new_batch_size = - self.size as f64 * TARGET_DURATION.as_millis() as f64 / duration.as_millis() as f64; + self.size as f64 * TARGET_DURATION.as_millis() as f64 / duration as f64; self.size = (2 * self.size).min(new_batch_size.round() as i64); } } diff --git a/store/postgres/src/deployment.rs b/store/postgres/src/deployment.rs index b18737ecbc9..1c8f31c384d 100644 --- a/store/postgres/src/deployment.rs +++ b/store/postgres/src/deployment.rs @@ -1024,9 +1024,10 @@ pub fn set_earliest_block( Ok(()) } -/// Lock the row for `site` in `subgraph_deployment` for update. This lock -/// is used to coordinate the changes that the subgraph writer makes with -/// changes that other parts of the system, in particular, pruning make +/// Lock the row for `site` in `subgraph_deployment` for update for the +/// remainder of the current transaction. This lock is used to coordinate +/// the changes that the subgraph writer makes with changes that other parts +/// of the system, in particular, pruning make // see also: deployment-lock-for-update pub fn lock(conn: &PgConnection, site: &Site) -> Result<(), StoreError> { use subgraph_deployment as d; diff --git a/store/postgres/src/relational/prune.rs b/store/postgres/src/relational/prune.rs index c942ff06760..fcc04b76567 100644 --- a/store/postgres/src/relational/prune.rs +++ b/store/postgres/src/relational/prune.rs @@ -226,6 +226,32 @@ impl Layout { /// that they will not be modified in any way while pruning is running. /// Only tables where the ratio of entities to entity versions is below /// `prune_ratio` will actually be pruned. + /// + /// The strategy for `prune_by_copying` is to copy all data that is + /// needed to respond to queries at block heights at or after + /// `earliest_block` to a new table and then to replace the existing + /// tables with these new tables atomically in a transaction. Copying + /// happens in two stages: we first copy data for final blocks without + /// blocking writes, and then copy data for nonfinal blocks. The latter + /// blocks writes by taking a lock on the row for the deployment in + /// `subgraph_deployment` (via `deployment::lock`) The process for + /// switching to the new tables needs to take the naming of various + /// database objects that Postgres creates automatically into account so + /// that they all have the same names as the original objects to ensure + /// that pruning can be done again without risking name clashes. + /// + /// The reason this strategy works well when a lot (or even the + /// majority) of the data needs to be removed is that in the more + /// straightforward strategy of simply deleting unneeded data, accessing + /// the remaining data becomes very inefficient since it is scattered + /// over a large number of pages, often with just one row per page. We + /// would therefore need to do a full vacuum of the tables after + /// deleting which effectively copies the remaining data into new + /// tables. But a full vacuum takes an `access exclusive` lock which + /// prevents both reads and writes to the table, which means it would + /// also block queries to the deployment, often for extended periods of + /// time. The `prune_by_copying` strategy never blocks reads, it only + /// ever blocks writes. pub fn prune_by_copying( &self, _logger: &Logger, From b212bc42b10f8abd0aaa99f3233e6148a01e45f5 Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Mon, 19 Sep 2022 14:50:06 +0200 Subject: [PATCH 015/253] server-json-rpc: replace jsonrpc with jsonrpsee (#3949) --- Cargo.lock | 106 +++++++---- graph/src/components/server/admin.rs | 19 -- graph/src/components/server/mod.rs | 3 - graph/src/lib.rs | 1 - node/src/main.rs | 3 +- server/json-rpc/Cargo.toml | 3 +- server/json-rpc/src/lib.rs | 272 +++++++++++---------------- 7 files changed, 188 insertions(+), 219 deletions(-) delete mode 100644 graph/src/components/server/admin.rs diff --git a/Cargo.lock b/Cargo.lock index 0ea2c3b8d44..b81d60ae4e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,6 +244,15 @@ dependencies = [ "base64", ] +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + [[package]] name = "bigdecimal" version = "0.1.2" @@ -1839,8 +1848,7 @@ name = "graph-server-json-rpc" version = "0.27.0" dependencies = [ "graph", - "jsonrpc-http-server", - "lazy_static", + "jsonrpsee", "serde", ] @@ -2058,13 +2066,13 @@ dependencies = [ [[package]] name = "http" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 0.4.7", + "itoa 1.0.1", ] [[package]] @@ -2368,39 +2376,76 @@ dependencies = [ ] [[package]] -name = "jsonrpc-http-server" -version = "18.0.0" +name = "jsonrpsee" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" +checksum = "8bd0d559d5e679b1ab2f869b486a11182923863b1b3ee8b421763cdd707b783a" dependencies = [ - "futures 0.3.16", - "hyper", - "jsonrpc-core", - "jsonrpc-server-utils", - "log", - "net2", - "parking_lot 0.11.2", - "unicase", + "jsonrpsee-core", + "jsonrpsee-http-server", + "jsonrpsee-types", ] [[package]] -name = "jsonrpc-server-utils" -version = "18.0.0" +name = "jsonrpsee-core" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" +checksum = "f3dc3e9cf2ba50b7b1d7d76a667619f82846caa39e8e8daa8a4962d74acaddca" dependencies = [ - "bytes", - "futures 0.3.16", + "anyhow", + "arrayvec 0.7.2", + "async-trait", + "beef", + "futures-channel", + "futures-util", "globset", - "jsonrpc-core", + "http", + "hyper", + "jsonrpsee-types", "lazy_static", - "log", + "parking_lot 0.12.1", + "rand", + "rustc-hash", + "serde", + "serde_json", + "thiserror", "tokio", - "tokio-stream", - "tokio-util 0.6.7", + "tracing", "unicase", ] +[[package]] +name = "jsonrpsee-http-server" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03802f0373a38c2420c70b5144742d800b509e2937edc4afb116434f07120117" +dependencies = [ + "futures-channel", + "futures-util", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e290bba767401b646812f608c099b922d8142603c9e73a50fb192d3ac86f4a0d" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "keccak" version = "0.1.0" @@ -2664,17 +2709,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "net2" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi", -] - [[package]] name = "never" version = "0.1.0" diff --git a/graph/src/components/server/admin.rs b/graph/src/components/server/admin.rs deleted file mode 100644 index f160982eb4e..00000000000 --- a/graph/src/components/server/admin.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::io; -use std::sync::Arc; - -use crate::prelude::Logger; -use crate::prelude::NodeId; - -/// Common trait for JSON-RPC admin server implementations. -pub trait JsonRpcServer

{ - type Server; - - fn serve( - port: u16, - http_port: u16, - ws_port: u16, - provider: Arc

, - node_id: NodeId, - logger: Logger, - ) -> Result; -} diff --git a/graph/src/components/server/mod.rs b/graph/src/components/server/mod.rs index c1af2ceda30..b0a510c26a6 100644 --- a/graph/src/components/server/mod.rs +++ b/graph/src/components/server/mod.rs @@ -4,9 +4,6 @@ pub mod query; /// Component for running GraphQL subscriptions over WebSockets. pub mod subscription; -/// Component for the JSON-RPC admin API. -pub mod admin; - /// Component for the index node server. pub mod index_node; diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 8d8073fc013..553f1343e13 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -114,7 +114,6 @@ pub mod prelude { CounterVec, Gauge, GaugeVec, Histogram, HistogramOpts, HistogramVec, MetricsRegistry, Opts, PrometheusError, Registry, }; - pub use crate::components::server::admin::JsonRpcServer; pub use crate::components::server::index_node::IndexNodeServer; pub use crate::components::server::metrics::MetricsServer; pub use crate::components::server::query::GraphQLServer; diff --git a/node/src/main.rs b/node/src/main.rs index 098dd1a4e36..e8c867a2a69 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -10,7 +10,7 @@ use graph::data::graphql::effort::LoadManager; use graph::env::EnvVars; use graph::firehose::{FirehoseEndpoints, FirehoseNetworks}; use graph::log::logger; -use graph::prelude::{IndexNodeServer as _, JsonRpcServer as _, *}; +use graph::prelude::{IndexNodeServer as _, *}; use graph::prometheus::Registry; use graph::url::Url; use graph_chain_arweave::{self as arweave, Block as ArweaveBlock}; @@ -460,6 +460,7 @@ async fn main() { node_id.clone(), logger.clone(), ) + .await .expect("failed to start JSON-RPC admin server"); // Let the server run forever. diff --git a/server/json-rpc/Cargo.toml b/server/json-rpc/Cargo.toml index 829a2b5ad47..7e7e4d9afa0 100644 --- a/server/json-rpc/Cargo.toml +++ b/server/json-rpc/Cargo.toml @@ -5,6 +5,5 @@ edition = "2021" [dependencies] graph = { path = "../../graph" } -jsonrpc-http-server = "18.0.0" -lazy_static = "1.2.0" +jsonrpsee = { version = "0.15.1", features = ["http-server"] } serde = "1.0" diff --git a/server/json-rpc/src/lib.rs b/server/json-rpc/src/lib.rs index d076a8d1c16..c720905345e 100644 --- a/server/json-rpc/src/lib.rs +++ b/server/json-rpc/src/lib.rs @@ -1,49 +1,80 @@ -extern crate graph; -extern crate jsonrpc_http_server; -extern crate lazy_static; -extern crate serde; - -use graph::prelude::serde_json; -use graph::prelude::{JsonRpcServer as JsonRpcServerTrait, *}; -use jsonrpc_http_server::{ - jsonrpc_core::{self, Compatibility, IoHandler, Params, Value}, - RestApi, Server, ServerBuilder, -}; +use graph::prelude::{Value as GraphValue, *}; +use jsonrpsee::core::Error as JsonRpcError; +use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; +use jsonrpsee::types::error::CallError; +use jsonrpsee::types::ErrorObject; +use jsonrpsee::RpcModule; +use serde_json::{self, Value as JsonValue}; use std::collections::BTreeMap; -use std::io; -use std::net::{Ipv4Addr, SocketAddrV4}; +use std::net::{Ipv4Addr, SocketAddr}; -const JSON_RPC_DEPLOY_ERROR: i64 = 0; -const JSON_RPC_REMOVE_ERROR: i64 = 1; -const JSON_RPC_CREATE_ERROR: i64 = 2; -const JSON_RPC_REASSIGN_ERROR: i64 = 3; +type JsonRpcResult = Result; -#[derive(Debug, Deserialize)] -struct SubgraphCreateParams { - name: SubgraphName, +pub struct JsonRpcServer { + // TODO: in the future we might want to have some sort of async drop to stop + // the server. For now, we're just letting it run it forever. + _handle: HttpServerHandle, } -#[derive(Debug, Deserialize)] -struct SubgraphDeployParams { - name: SubgraphName, - ipfs_hash: DeploymentHash, - node_id: Option, - debug_fork: Option, -} +impl JsonRpcServer { + pub async fn serve( + port: u16, + http_port: u16, + ws_port: u16, + registrar: Arc, + node_id: NodeId, + logger: Logger, + ) -> JsonRpcResult + where + R: SubgraphRegistrar, + { + let logger = logger.new(o!("component" => "JsonRpcServer")); -#[derive(Debug, Deserialize)] -struct SubgraphRemoveParams { - name: SubgraphName, -} + info!( + logger, + "Starting JSON-RPC admin server at: http://localhost:{}", port + ); -#[derive(Debug, Deserialize)] -struct SubgraphReassignParams { - ipfs_hash: DeploymentHash, - node_id: NodeId, + let state = ServerState { + registrar, + http_port, + ws_port, + node_id, + logger, + }; + + let socket_addr: SocketAddr = (Ipv4Addr::new(0, 0, 0, 0), port).into(); + let http_server = HttpServerBuilder::default().build(socket_addr).await?; + + let mut rpc_module = RpcModule::new(state); + rpc_module + .register_async_method("subgraph_create", |params, state| async move { + state.create_handler(params.parse()?).await + }) + .unwrap(); + rpc_module + .register_async_method("subgraph_deploy", |params, state| async move { + state.deploy_handler(params.parse()?).await + }) + .unwrap(); + rpc_module + .register_async_method("subgraph_remove", |params, state| async move { + state.remove_handler(params.parse()?).await + }) + .unwrap(); + rpc_module + .register_async_method("subgraph_reassign", |params, state| async move { + state.reassign_handler(params.parse()?).await + }) + .unwrap(); + + let _handle = http_server.start(rpc_module)?; + Ok(Self { _handle }) + } } -pub struct JsonRpcServer { +struct ServerState { registrar: Arc, http_port: u16, ws_port: u16, @@ -51,12 +82,14 @@ pub struct JsonRpcServer { logger: Logger, } -impl JsonRpcServer { +impl ServerState { + const DEPLOY_ERROR: i64 = 0; + const REMOVE_ERROR: i64 = 1; + const CREATE_ERROR: i64 = 2; + const REASSIGN_ERROR: i64 = 3; + /// Handler for the `subgraph_create` endpoint. - async fn create_handler( - &self, - params: SubgraphCreateParams, - ) -> Result { + async fn create_handler(&self, params: SubgraphCreateParams) -> JsonRpcResult { info!(&self.logger, "Received subgraph_create request"; "params" => format!("{:?}", params)); match self.registrar.create_subgraph(params.name.clone()).await { @@ -67,17 +100,14 @@ impl JsonRpcServer { &self.logger, "subgraph_create", e, - JSON_RPC_CREATE_ERROR, + Self::CREATE_ERROR, params, )), } } /// Handler for the `subgraph_deploy` endpoint. - async fn deploy_handler( - &self, - params: SubgraphDeployParams, - ) -> Result { + async fn deploy_handler(&self, params: SubgraphDeployParams) -> JsonRpcResult { info!(&self.logger, "Received subgraph_deploy request"; "params" => format!("{:?}", params)); let node_id = params.node_id.clone().unwrap_or(self.node_id.clone()); @@ -101,17 +131,14 @@ impl JsonRpcServer { &self.logger, "subgraph_deploy", e, - JSON_RPC_DEPLOY_ERROR, + Self::DEPLOY_ERROR, params, )), } } /// Handler for the `subgraph_remove` endpoint. - async fn remove_handler( - &self, - params: SubgraphRemoveParams, - ) -> Result { + async fn remove_handler(&self, params: SubgraphRemoveParams) -> JsonRpcResult { info!(&self.logger, "Received subgraph_remove request"; "params" => format!("{:?}", params)); match self.registrar.remove_subgraph(params.name.clone()).await { @@ -120,20 +147,15 @@ impl JsonRpcServer { &self.logger, "subgraph_remove", e, - JSON_RPC_REMOVE_ERROR, + Self::REMOVE_ERROR, params, )), } } /// Handler for the `subgraph_assign` endpoint. - async fn reassign_handler( - &self, - params: SubgraphReassignParams, - ) -> Result { - let logger = self.logger.clone(); - - info!(logger, "Received subgraph_reassignment request"; "params" => format!("{:?}", params)); + async fn reassign_handler(&self, params: SubgraphReassignParams) -> JsonRpcResult { + info!(&self.logger, "Received subgraph_reassignment request"; "params" => format!("{:?}", params)); match self .registrar @@ -142,100 +164,23 @@ impl JsonRpcServer { { Ok(_) => Ok(Value::Null), Err(e) => Err(json_rpc_error( - &logger, + &self.logger, "subgraph_reassign", e, - JSON_RPC_REASSIGN_ERROR, + Self::REASSIGN_ERROR, params, )), } } } -impl JsonRpcServerTrait for JsonRpcServer -where - R: SubgraphRegistrar, -{ - type Server = Server; - - fn serve( - port: u16, - http_port: u16, - ws_port: u16, - registrar: Arc, - node_id: NodeId, - logger: Logger, - ) -> Result { - let logger = logger.new(o!("component" => "JsonRpcServer")); - - info!( - logger, - "Starting JSON-RPC admin server at: http://localhost:{}", port - ); - - let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port); - - let mut handler = IoHandler::with_compatibility(Compatibility::Both); - - let arc_self = Arc::new(JsonRpcServer { - registrar, - http_port, - ws_port, - node_id, - logger, - }); - - let me = arc_self.clone(); - handler.add_method("subgraph_create", move |params: Params| { - let me = me.clone(); - async move { - let params = params.parse()?; - me.create_handler(params).await - } - }); - - let me = arc_self.clone(); - handler.add_method("subgraph_deploy", move |params: Params| { - let me = me.clone(); - async move { - let params = params.parse()?; - me.deploy_handler(params).await - } - }); - - let me = arc_self.clone(); - handler.add_method("subgraph_remove", move |params: Params| { - let me = me.clone(); - async move { - let params = params.parse()?; - me.remove_handler(params).await - } - }); - - let me = arc_self; - handler.add_method("subgraph_reassign", move |params: Params| { - let me = me.clone(); - async move { - let params = params.parse()?; - me.reassign_handler(params).await - } - }); - - ServerBuilder::new(handler) - // Enable REST API: - // POST /// - .rest_api(RestApi::Secure) - .start_http(&addr.into()) - } -} - fn json_rpc_error( logger: &Logger, operation: &str, e: SubgraphRegistrarError, code: i64, params: impl std::fmt::Debug, -) -> jsonrpc_core::Error { +) -> JsonRpcError { error!(logger, "{} failed", operation; "error" => format!("{:?}", e), "params" => format!("{:?}", params)); @@ -246,26 +191,14 @@ fn json_rpc_error( e.to_string() }; - jsonrpc_core::Error { - code: jsonrpc_core::ErrorCode::ServerError(code), + JsonRpcError::Call(CallError::Custom(ErrorObject::owned( + code as _, message, - data: None, - } -} - -pub fn parse_response(response: Value) -> Result<(), jsonrpc_core::Error> { - // serde deserialization of the `id` field to an `Id` struct is somehow - // incompatible with the `arbitrary-precision` feature which we use, so we - // need custom parsing logic. - let object = response.as_object().unwrap(); - if let Some(error) = object.get("error") { - Err(serde_json::from_value(error.clone()).unwrap()) - } else { - Ok(()) - } + None::, + ))) } -fn subgraph_routes(name: &SubgraphName, http_port: u16, ws_port: u16) -> Value { +fn subgraph_routes(name: &SubgraphName, http_port: u16, ws_port: u16) -> JsonValue { let http_base_url = ENV_VARS .external_http_base_url .clone() @@ -288,5 +221,30 @@ fn subgraph_routes(name: &SubgraphName, http_port: u16, ws_port: u16) -> Value { "subscriptions", format!("{}/subgraphs/name/{}", ws_base_url, name), ); - jsonrpc_core::to_value(map).unwrap() + + serde_json::to_value(map).expect("invalid subgraph routes") +} + +#[derive(Debug, Deserialize)] +struct SubgraphCreateParams { + name: SubgraphName, +} + +#[derive(Debug, Deserialize)] +struct SubgraphDeployParams { + name: SubgraphName, + ipfs_hash: DeploymentHash, + node_id: Option, + debug_fork: Option, +} + +#[derive(Debug, Deserialize)] +struct SubgraphRemoveParams { + name: SubgraphName, +} + +#[derive(Debug, Deserialize)] +struct SubgraphReassignParams { + ipfs_hash: DeploymentHash, + node_id: NodeId, } From 7356e0b169f55bd986307b5564d7ffa948899a8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 14:09:35 +0100 Subject: [PATCH 016/253] build(deps): bump pretty_assertions from 1.2.1 to 1.3.0 (#3906) Bumps [pretty_assertions](https://github.com/rust-pretty-assertions/rust-pretty-assertions) from 1.2.1 to 1.3.0. - [Release notes](https://github.com/rust-pretty-assertions/rust-pretty-assertions/releases) - [Changelog](https://github.com/rust-pretty-assertions/rust-pretty-assertions/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-pretty-assertions/rust-pretty-assertions/compare/v1.2.1...v1.3.0) --- updated-dependencies: - dependency-name: pretty_assertions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 +++++++++--- core/Cargo.toml | 2 +- graphql/Cargo.toml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b81d60ae4e7..e1997353802 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3099,14 +3099,14 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" +checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" dependencies = [ - "ansi_term", "ctor", "diff", "output_vt100", + "yansi", ] [[package]] @@ -5383,6 +5383,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zstd" version = "0.6.1+zstd.1.4.9" diff --git a/core/Cargo.toml b/core/Cargo.toml index 557d791d4be..94f16819c79 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -36,5 +36,5 @@ graph-mock = { path = "../mock" } test-store = { path = "../store/test-store" } hex = "0.4.3" graphql-parser = "0.4.0" -pretty_assertions = "1.2.1" +pretty_assertions = "1.3.0" anyhow = "1.0" diff --git a/graphql/Cargo.toml b/graphql/Cargo.toml index 44c8313bcee..a973e20701e 100644 --- a/graphql/Cargo.toml +++ b/graphql/Cargo.toml @@ -19,6 +19,6 @@ anyhow = "1.0" async-recursion = "1.0.0" [dev-dependencies] -pretty_assertions = "1.2.1" +pretty_assertions = "1.3.0" test-store = { path = "../store/test-store" } graph-chain-ethereum = { path = "../chain/ethereum" } From 47937cf85fcc4abb8f0b00194a7cc5365223a69e Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Mon, 19 Sep 2022 18:12:35 -0700 Subject: [PATCH 017/253] store: Make it possible to restart a failed writer So far, if the writer for a subgraph encounterd an error, it would stick around as 'poisoned' for as long as the process lived. That made it impossible to restart a subgraph (possibly after clearing some external error condition) With this change, `SubgraphStore.writable` will create a new writer if the old one has been poisoned. --- graph/src/components/store/traits.rs | 7 ++++++- store/postgres/src/subgraph_store.rs | 7 ++++++- store/postgres/src/writable.rs | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/graph/src/components/store/traits.rs b/graph/src/components/store/traits.rs index 158e75aa87d..1749b8bea09 100644 --- a/graph/src/components/store/traits.rs +++ b/graph/src/components/store/traits.rs @@ -130,7 +130,12 @@ pub trait SubgraphStore: Send + Sync + 'static { /// Return a `WritableStore` that is used for indexing subgraphs. Only /// code that is part of indexing a subgraph should ever use this. The /// `logger` will be used to log important messages related to the - /// subgraph + /// subgraph. + /// + /// This function should only be called in situations where no + /// assumptions about the in-memory state of writing has been made; in + /// particular, no assumptions about whether previous writes have + /// actually been committed or not. async fn writable( self: Arc, logger: Logger, diff --git a/store/postgres/src/subgraph_store.rs b/store/postgres/src/subgraph_store.rs index 19bc747f298..1c883579879 100644 --- a/store/postgres/src/subgraph_store.rs +++ b/store/postgres/src/subgraph_store.rs @@ -1232,7 +1232,12 @@ impl SubgraphStoreTrait for SubgraphStore { // idempotent and there is ever only one `WritableStore` for any // deployment if let Some(writable) = self.writables.lock().unwrap().get(&deployment) { - return Ok(writable.cheap_clone()); + // A poisoned writable will not write anything anymore; we + // discard it and create a new one that is properly initialized + // according to the state in the database. + if !writable.poisoned() { + return Ok(writable.cheap_clone()); + } } // Ideally the lower level functions would be asyncified. diff --git a/store/postgres/src/writable.rs b/store/postgres/src/writable.rs index 800a0ced080..417fe368428 100644 --- a/store/postgres/src/writable.rs +++ b/store/postgres/src/writable.rs @@ -791,6 +791,10 @@ impl Queue { Ok(dds) } + + fn poisoned(&self) -> bool { + self.poisoned.load(Ordering::SeqCst) + } } /// A shim to allow bypassing any pipelined store handling if need be @@ -908,6 +912,13 @@ impl Writer { Writer::Async(queue) => queue.load_dynamic_data_sources(manifest_idx_and_name).await, } } + + fn poisoned(&self) -> bool { + match self { + Writer::Sync(_) => false, + Writer::Async(queue) => queue.poisoned(), + } + } } pub struct WritableStore { @@ -941,6 +952,10 @@ impl WritableStore { writer, }) } + + pub(crate) fn poisoned(&self) -> bool { + self.writer.poisoned() + } } impl ReadStore for WritableStore { From 27c534849d4537e10fd0c3b3a51ab4cfb5a2bcc6 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Mon, 19 Sep 2022 08:58:22 -0700 Subject: [PATCH 018/253] store: Log more detail during connection setup --- store/postgres/src/connection_pool.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/store/postgres/src/connection_pool.rs b/store/postgres/src/connection_pool.rs index d1f48a5bf48..3e665a1abd9 100644 --- a/store/postgres/src/connection_pool.rs +++ b/store/postgres/src/connection_pool.rs @@ -977,6 +977,7 @@ impl PoolInner { let pool = self.clone(); let conn = self.get().map_err(|_| StoreError::DatabaseUnavailable)?; + let start = Instant::now(); advisory_lock::lock_migration(&conn) .unwrap_or_else(|err| die(&pool.logger, "failed to get migration lock", &err)); let result = pool @@ -984,10 +985,12 @@ impl PoolInner { .and_then(|()| migrate_schema(&pool.logger, &conn)) .and_then(|()| pool.map_primary()) .and_then(|()| pool.map_metadata(servers.as_ref())); + debug!(&pool.logger, "Release migration lock"); advisory_lock::unlock_migration(&conn).unwrap_or_else(|err| { die(&pool.logger, "failed to release migration lock", &err); }); result.unwrap_or_else(|err| die(&pool.logger, "migrations failed", &err)); + debug!(&pool.logger, "Setup finished"; "setup_time_s" => start.elapsed().as_secs()); Ok(()) } @@ -1047,6 +1050,7 @@ impl PoolInner { // servers to ourselves. The mapping is recreated on every server start // so that we pick up possible schema changes in the mappings fn map_metadata(&self, servers: &[ForeignServer]) -> Result<(), StoreError> { + info!(&self.logger, "Mapping metadata"); let conn = self.get()?; conn.transaction(|| { for server in servers.iter().filter(|server| server.shard != self.shard) { From f5fac18ec1f1d722828b59204c43a3dd61e8db74 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 16 Sep 2022 16:06:39 -0700 Subject: [PATCH 019/253] graphql: Remove validation cache Initial testing suggests that, after the recent improvement in graphql-tools, validations are now fast enough to not need such a cache. --- graphql/src/execution/query.rs | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/graphql/src/execution/query.rs b/graphql/src/execution/query.rs index f0d4edd5b82..ddc09598b26 100644 --- a/graphql/src/execution/query.rs +++ b/graphql/src/execution/query.rs @@ -2,10 +2,8 @@ use graph::data::graphql::DocumentExt as _; use graph::data::value::Object; use graphql_parser::Pos; use graphql_tools::validation::rules::*; -use graphql_tools::validation::utils::ValidationError; use graphql_tools::validation::validate::{validate, ValidationPlan}; use lazy_static::lazy_static; -use parking_lot::Mutex; use std::collections::{BTreeMap, HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; @@ -27,11 +25,6 @@ use crate::schema::ast::{self as sast}; use crate::values::coercion; use crate::{execution::get_field, schema::api::ErrorPolicy}; -lazy_static! { - static ref GRAPHQL_VALIDATION_CACHE: Mutex>> = - Mutex::new(HashMap::>::new()); -} - lazy_static! { static ref GRAPHQL_VALIDATION_PLAN: ValidationPlan = ValidationPlan::from(if !ENV_VARS.graphql.enable_validations { @@ -149,27 +142,11 @@ fn validate_query( query: &GraphDataQuery, document: &s::Document, ) -> Result<(), Vec> { - let errors = { - let cached = GRAPHQL_VALIDATION_CACHE - .lock() - .get(&query.shape_hash) - .cloned(); - match cached { - Some(cached) => cached, - None => { - let validation_errors = - validate(&document, &query.document, &GRAPHQL_VALIDATION_PLAN); - GRAPHQL_VALIDATION_CACHE - .lock() - .insert(query.shape_hash, validation_errors.clone()); - validation_errors - } - } - }; + let validation_errors = validate(&document, &query.document, &GRAPHQL_VALIDATION_PLAN); - if !errors.is_empty() { + if !validation_errors.is_empty() { if !ENV_VARS.graphql.silent_graphql_validations { - return Err(errors + return Err(validation_errors .into_iter() .map(|e| { QueryExecutionError::ValidationError( @@ -184,7 +161,7 @@ fn validate_query( "GraphQL Validation failure"; "query" => &query.query_text, "variables" => &query.variables_text, - "errors" => format!("[{:?}]", errors.iter().map(|e| e.message.clone()).collect::>().join(", ")) + "errors" => format!("[{:?}]", validation_errors.iter().map(|e| e.message.clone()).collect::>().join(", ")) ); } } From e4bdba2dda4649b0f2bec973d81ec81f473d8744 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Wed, 21 Sep 2022 12:12:59 +0100 Subject: [PATCH 020/253] fix false positive POI (#3951) --- core/src/subgraph/trigger_processor.rs | 22 ++++-- store/postgres/src/subgraph_store.rs | 2 +- .../dynamic-data-source/abis/Contract.abi | 33 ++++++++ .../dynamic-data-source/package.json | 25 ++++++ .../dynamic-data-source/schema.graphql | 4 + .../dynamic-data-source/src/mapping.ts | 16 ++++ .../dynamic-data-source/subgraph.yaml | 42 ++++++++++ tests/integration-tests/package.json | 1 + tests/src/fixture.rs | 19 +++-- tests/tests/runner.rs | 78 ++++++++++++++++++- 10 files changed, 225 insertions(+), 17 deletions(-) create mode 100644 tests/integration-tests/dynamic-data-source/abis/Contract.abi create mode 100644 tests/integration-tests/dynamic-data-source/package.json create mode 100644 tests/integration-tests/dynamic-data-source/schema.graphql create mode 100644 tests/integration-tests/dynamic-data-source/src/mapping.ts create mode 100644 tests/integration-tests/dynamic-data-source/subgraph.yaml diff --git a/core/src/subgraph/trigger_processor.rs b/core/src/subgraph/trigger_processor.rs index f8d3e78122b..58e6c4a8806 100644 --- a/core/src/subgraph/trigger_processor.rs +++ b/core/src/subgraph/trigger_processor.rs @@ -3,7 +3,7 @@ use graph::blockchain::Blockchain; use graph::cheap_clone::CheapClone; use graph::components::store::SubgraphFork; use graph::components::subgraph::{MappingError, SharedProofOfIndexing}; -use graph::data_source::TriggerData; +use graph::data_source::{MappingTrigger, TriggerData, TriggerWithHandler}; use graph::prelude::tokio::time::Instant; use graph::prelude::{ BlockState, RuntimeHost, RuntimeHostBuilder, SubgraphInstanceMetrics, TriggerProcessor, @@ -33,11 +33,7 @@ where ) -> Result, MappingError> { let error_count = state.deterministic_errors.len(); - if let Some(proof_of_indexing) = proof_of_indexing { - proof_of_indexing - .borrow_mut() - .start_handler(causality_region); - } + let mut host_mapping: Vec<(&T::Host, TriggerWithHandler>)> = vec![]; for host in hosts { let mapping_trigger = match host.match_and_decode(trigger, block, logger)? { @@ -48,6 +44,20 @@ where None => continue, }; + host_mapping.push((&host, mapping_trigger)); + } + + if host_mapping.is_empty() { + return Ok(state); + } + + if let Some(proof_of_indexing) = proof_of_indexing { + proof_of_indexing + .borrow_mut() + .start_handler(causality_region); + } + + for (host, mapping_trigger) in host_mapping { let start = Instant::now(); state = host .process_mapping_trigger( diff --git a/store/postgres/src/subgraph_store.rs b/store/postgres/src/subgraph_store.rs index 1c883579879..9e1c72040dd 100644 --- a/store/postgres/src/subgraph_store.rs +++ b/store/postgres/src/subgraph_store.rs @@ -228,7 +228,7 @@ impl SubgraphStore { } } - pub(crate) async fn get_proof_of_indexing( + pub async fn get_proof_of_indexing( &self, id: &DeploymentHash, indexer: &Option

, diff --git a/tests/integration-tests/dynamic-data-source/abis/Contract.abi b/tests/integration-tests/dynamic-data-source/abis/Contract.abi new file mode 100644 index 00000000000..02da1a9e7f3 --- /dev/null +++ b/tests/integration-tests/dynamic-data-source/abis/Contract.abi @@ -0,0 +1,33 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "x", + "type": "uint16" + } + ], + "name": "Trigger", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "x", + "type": "uint16" + } + ], + "name": "emitTrigger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/tests/integration-tests/dynamic-data-source/package.json b/tests/integration-tests/dynamic-data-source/package.json new file mode 100644 index 00000000000..7d9cead2c9e --- /dev/null +++ b/tests/integration-tests/dynamic-data-source/package.json @@ -0,0 +1,25 @@ +{ + "name": "dynamic-data-source", + "version": "0.1.0", + "scripts": { + "build-contracts": "../common/build-contracts.sh", + "codegen": "graph codegen", + "test": "yarn build-contracts && truffle test --compile-none --network test", + "create:test": "graph create test/dynamic-data-source --node $GRAPH_NODE_ADMIN_URI", + "deploy:test": "graph deploy test/dynamic-data-source --version-label v0.0.1 --ipfs $IPFS_URI --node $GRAPH_NODE_ADMIN_URI" + }, + "devDependencies": { + "@graphprotocol/graph-cli": "https://github.com/graphprotocol/graph-cli#main", + "@graphprotocol/graph-ts": "https://github.com/graphprotocol/graph-ts#main", + "solc": "^0.8.2" + }, + "dependencies": { + "@truffle/contract": "^4.3", + "@truffle/hdwallet-provider": "^1.2", + "apollo-fetch": "^0.7.0", + "babel-polyfill": "^6.26.0", + "babel-register": "^6.26.0", + "gluegun": "^4.6.1", + "truffle": "^5.2" + } +} diff --git a/tests/integration-tests/dynamic-data-source/schema.graphql b/tests/integration-tests/dynamic-data-source/schema.graphql new file mode 100644 index 00000000000..e5356472879 --- /dev/null +++ b/tests/integration-tests/dynamic-data-source/schema.graphql @@ -0,0 +1,4 @@ +type Foo @entity { + id: ID! + value: String! +} diff --git a/tests/integration-tests/dynamic-data-source/src/mapping.ts b/tests/integration-tests/dynamic-data-source/src/mapping.ts new file mode 100644 index 00000000000..ec69d2136b3 --- /dev/null +++ b/tests/integration-tests/dynamic-data-source/src/mapping.ts @@ -0,0 +1,16 @@ +import { Trigger } from "../generated/Contract/Contract"; +import {Foo} from "../generated/schema"; + + +export function handleTrigger(event: Trigger): void { + let id = `${event.block.hash.toHexString()}${event.address.toHexString()}`; + let foo = new Foo(id); + foo.save(); +} + + + + + + + diff --git a/tests/integration-tests/dynamic-data-source/subgraph.yaml b/tests/integration-tests/dynamic-data-source/subgraph.yaml new file mode 100644 index 00000000000..3f7efbaaca0 --- /dev/null +++ b/tests/integration-tests/dynamic-data-source/subgraph.yaml @@ -0,0 +1,42 @@ +specVersion: 0.0.4 +schema: + file: ./schema.graphql +dataSources: + - kind: ethereum/contract + name: Contract + network: test + source: + address: "0xCfEB869F69431e42cdB54A4F4f105C19C080A601" + abi: Contract + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + abis: + - name: Contract + file: ./abis/Contract.abi + entities: + - Call + eventHandlers: + - event: Trigger(uint16) + handler: handleTrigger + file: ./src/mapping.ts +templates: + - kind: ethereum/contract + name: Dynamic + network: test + source: + abi: Contract + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + abis: + - name: Contract + file: ./abis/Contract.abi + entities: + - Call + eventHandlers: + - event: Trigger(uint16) + handler: handleTrigger + file: ./src/mapping.ts diff --git a/tests/integration-tests/package.json b/tests/integration-tests/package.json index 845bd090977..093263041c2 100644 --- a/tests/integration-tests/package.json +++ b/tests/integration-tests/package.json @@ -12,6 +12,7 @@ "remove-then-update", "typename", "value-roundtrip", + "dynamic-data-source", "file-data-sources" ] } \ No newline at end of file diff --git a/tests/src/fixture.rs b/tests/src/fixture.rs index 2a5f2b7562f..949b3f5ef38 100644 --- a/tests/src/fixture.rs +++ b/tests/src/fixture.rs @@ -20,7 +20,7 @@ use graph::cheap_clone::CheapClone; use graph::components::store::{BlockStore, DeploymentLocator}; use graph::data::graphql::effort::LoadManager; use graph::data::query::{Query, QueryTarget}; -use graph::env::ENV_VARS; +use graph::env::EnvVars; use graph::ipfs_client::IpfsClient; use graph::prelude::ethabi::ethereum_types::H256; use graph::prelude::{ @@ -28,6 +28,7 @@ use graph::prelude::{ MetricsRegistry, NodeId, QueryError, SubgraphAssignmentProvider, SubgraphName, SubgraphRegistrar, SubgraphStore as _, SubgraphVersionSwitchingMode, }; +use graph::slog::crit; use graph_core::polling_monitor::ipfs_service::IpfsService; use graph_core::{ LinkResolver, SubgraphAssignmentProvider as IpfsSubgraphAssignmentProvider, @@ -38,7 +39,7 @@ use graph_mock::MockMetricsRegistry; use graph_node::manager::PanicSubscriptionManager; use graph_node::{config::Config, store_builder::StoreBuilder}; use graph_store_postgres::{ChainHeadUpdateListener, ChainStore, Store, SubgraphStore}; -use slog::{crit, info, Logger}; +use slog::{info, Logger}; use std::env::VarError; use std::pin::Pin; use std::sync::Arc; @@ -216,7 +217,13 @@ pub async fn setup( stores: &Stores, chain: Arc, graft_block: Option, + env_vars: Option, ) -> TestContext { + let env_vars = match env_vars { + Some(ev) => ev, + None => EnvVars::from_env().unwrap(), + }; + let logger = graph::log::logger(true); let logger_factory = LoggerFactory::new(logger.clone(), None); let mock_registry: Arc = Arc::new(MockMetricsRegistry::new()); @@ -229,7 +236,7 @@ pub async fn setup( let mut blockchain_map = BlockchainMap::new(); blockchain_map.insert(stores.network_name.clone(), chain); - let static_filters = ENV_VARS.experimental_static_filters; + let static_filters = env_vars.experimental_static_filters; let ipfs = IpfsClient::localhost(); let link_resolver = Arc::new(LinkResolver::new( @@ -238,9 +245,9 @@ pub async fn setup( )); let ipfs_service = IpfsService::new( ipfs, - ENV_VARS.mappings.max_ipfs_file_bytes as u64, - ENV_VARS.mappings.ipfs_timeout, - ENV_VARS.mappings.max_ipfs_concurrent_requests, + env_vars.mappings.max_ipfs_file_bytes as u64, + env_vars.mappings.ipfs_timeout, + env_vars.mappings.max_ipfs_concurrent_requests, ); let blockchain_map = Arc::new(blockchain_map); diff --git a/tests/tests/runner.rs b/tests/tests/runner.rs index 133be063034..8339c8aa841 100644 --- a/tests/tests/runner.rs +++ b/tests/tests/runner.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use cid::Cid; use graph::blockchain::{Block, BlockPtr}; +use graph::env::EnvVars; use graph::object; use graph::prelude::ethabi::ethereum_types::H256; use graph::prelude::{SubgraphAssignmentProvider, SubgraphName}; @@ -33,7 +34,15 @@ async fn data_source_revert() -> anyhow::Result<()> { }; let chain = Arc::new(chain(blocks.clone(), &stores).await); - let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain.clone(), None).await; + let ctx = fixture::setup( + subgraph_name.clone(), + &hash, + &stores, + chain.clone(), + None, + None, + ) + .await; let stop_block = test_ptr(2); ctx.start_and_sync_to(stop_block).await; @@ -51,7 +60,15 @@ async fn data_source_revert() -> anyhow::Result<()> { ) .await; let graft_block = Some(test_ptr(3)); - let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, graft_block).await; + let ctx = fixture::setup( + subgraph_name.clone(), + &hash, + &stores, + chain, + graft_block, + None, + ) + .await; let stop_block = test_ptr(4); ctx.start_and_sync_to(stop_block).await; @@ -97,7 +114,7 @@ async fn typename() -> anyhow::Result<()> { let stores = stores("./integration-tests/config.simple.toml").await; let chain = Arc::new(chain(blocks, &stores).await); - let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, None).await; + let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, None, None).await; ctx.start_and_sync_to(stop_block).await; @@ -122,7 +139,7 @@ async fn file_data_sources() { }; let stop_block = test_ptr(1); let chain = Arc::new(chain(blocks, &stores).await); - let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, None).await; + let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, None, None).await; ctx.start_and_sync_to(stop_block).await; // CID QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ is the file @@ -151,3 +168,56 @@ async fn file_data_sources() { let stop_block = test_ptr(2); ctx.start_and_sync_to(stop_block).await; } + +#[tokio::test] +async fn template_static_filters_false_positives() { + let stores = stores("./integration-tests/config.simple.toml").await; + + let subgraph_name = SubgraphName::new("dynamic-data-source").unwrap(); + let hash = { + let test_dir = format!("./integration-tests/{}", subgraph_name); + fixture::build_subgraph(&test_dir).await + }; + + let blocks = { + let block_0 = genesis(); + let block_1 = empty_block(block_0.ptr(), test_ptr(1)); + let block_2 = empty_block(block_1.ptr(), test_ptr(2)); + vec![block_0, block_1, block_2] + }; + let stop_block = test_ptr(1); + let chain = Arc::new(chain(blocks, &stores).await); + + let mut env_vars = EnvVars::default(); + env_vars.experimental_static_filters = true; + + let ctx = fixture::setup( + subgraph_name.clone(), + &hash, + &stores, + chain, + None, + Some(env_vars), + ) + .await; + ctx.start_and_sync_to(stop_block).await; + + let poi = ctx + .store + .get_proof_of_indexing(&ctx.deployment.hash, &None, test_ptr(1)) + .await + .unwrap(); + + // This check exists to prevent regression of https://github.com/graphprotocol/graph-node/issues/3963 + // when false positives go through the block stream, they should be discarded by + // `DataSource::match_and_decode`. The POI below is generated consistently from the empty + // POI table. If this fails it's likely that either the bug was re-introduced or there is + // a change in the POI infrastructure. + assert_eq!( + poi.unwrap(), + [ + 196, 173, 167, 52, 226, 19, 154, 61, 189, 94, 19, 229, 18, 7, 0, 252, 234, 49, 110, + 179, 105, 64, 16, 46, 25, 194, 83, 94, 195, 225, 56, 252 + ], + ); +} From 6387465040b98f926880f069c655b8f5ea1f1b4b Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 14 Sep 2022 11:43:34 -0700 Subject: [PATCH 021/253] docker: Restore old behvior around BLOCK_INGESTOR PR https://github.com/graphprotocol/graph-node/pull/3688 introduced a regression where the setting of BLOCK_INGESTOR had to be changed from unmangled to mangled. Fixes https://github.com/graphprotocol/graph-node/issues/3936 --- docker/start | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docker/start b/docker/start index 0c003ccefd1..435fbe5429f 100755 --- a/docker/start +++ b/docker/start @@ -80,17 +80,12 @@ run_graph_node() { } start_query_node() { + # Query nodes are never the block ingestor export DISABLE_BLOCK_INGESTOR=true run_graph_node } start_index_node() { - # Only the index node with the name set in BLOCK_INGESTOR should ingest - # blocks - if [[ ${node_id} != "${BLOCK_INGESTOR}" ]]; then - export DISABLE_BLOCK_INGESTOR=true - fi - run_graph_node } @@ -98,6 +93,14 @@ start_combined_node() { run_graph_node } +# Only the index node with the name set in BLOCK_INGESTOR should ingest +# blocks. For historical reasons, that name is set to the unmangled version +# of `node_id` and we need to check whether we are the block ingestor +# before possibly mangling the node_id. +if [[ ${node_id} != "${BLOCK_INGESTOR}" ]]; then + export DISABLE_BLOCK_INGESTOR=true +fi + # Allow operators to opt out of legacy character # restrictions on the node ID by setting enablement # variable to a non-zero length string: From d1004e631f3d9d02ffd1712543d791f11926bae3 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 20 Sep 2022 09:04:54 -0700 Subject: [PATCH 022/253] store: Be considerate of replicas when copying Copying of subgraphs can create a lot of data to be replicated, which can make replicas fall hopelessly behind. With this change, the copy code checks whether there are replicas that are too far behind and backs off of copying for a bit to give them a chance to catch up. --- store/postgres/src/catalog.rs | 25 +++++++++++++++++++++++++ store/postgres/src/copy.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 22bf3d941ef..99e1fbb604d 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -11,6 +11,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Write; use std::iter::FromIterator; use std::sync::Arc; +use std::time::Duration; use graph::prelude::anyhow::anyhow; use graph::{ @@ -616,3 +617,27 @@ pub fn stats(conn: &PgConnection, namespace: &Namespace) -> Result Result { + #[derive(Queryable, QueryableByName)] + struct Lag { + #[sql_type = "Nullable"] + ms: Option, + } + + let lag = sql_query( + "select extract(milliseconds from max(greatest(write_lag, flush_lag, replay_lag)))::int as ms \ + from pg_stat_replication", + ) + .get_result::(conn)?; + + let lag = lag + .ms + .map(|ms| if ms <= 0 { 0 } else { ms as u64 }) + .unwrap_or(0); + + Ok(Duration::from_millis(lag)) +} diff --git a/store/postgres/src/copy.rs b/store/postgres/src/copy.rs index 6c69664acfb..6454e8c04e0 100644 --- a/store/postgres/src/copy.rs +++ b/store/postgres/src/copy.rs @@ -38,7 +38,7 @@ use graph::{ }; use crate::{ - advisory_lock, + advisory_lock, catalog, dynds::DataSourcesTable, primary::{DeploymentId, Site}, }; @@ -54,6 +54,16 @@ const INITIAL_BATCH_SIZE_LIST: i64 = 100; const TARGET_DURATION: Duration = Duration::from_secs(5 * 60); const LOG_INTERVAL: Duration = Duration::from_secs(3 * 60); +/// If replicas are lagging by more than this, the copying code will pause +/// for a while to allow replicas to catch up +const MAX_REPLICATION_LAG: Duration = Duration::from_secs(60); +/// If replicas need to catch up, do not resume copying until the lag is +/// less than this +const ACCEPTABLE_REPLICATION_LAG: Duration = Duration::from_secs(30); +/// When replicas are lagging too much, sleep for this long before checking +/// the lag again +const REPLICATION_SLEEP: Duration = Duration::from_secs(10); + table! { subgraphs.copy_state(dst) { // deployment_schemas.id @@ -744,6 +754,24 @@ impl Connection { if table.is_cancelled(&self.conn)? { return Ok(Status::Cancelled); } + + // Pause copying if replication is lagging behind to avoid + // overloading replicas + let mut lag = catalog::replication_lag(&self.conn)?; + if lag > MAX_REPLICATION_LAG { + loop { + info!(&self.logger, + "Replicas are lagging too much; pausing copying for {}s to allow them to catch up", + REPLICATION_SLEEP.as_secs(); + "lag_s" => lag.as_secs()); + std::thread::sleep(REPLICATION_SLEEP); + lag = catalog::replication_lag(&self.conn)?; + if lag <= ACCEPTABLE_REPLICATION_LAG { + break; + } + } + } + let status = self.transaction(|conn| table.copy_batch(conn))?; if status == Status::Cancelled { return Ok(status); From 7fb996b471f6683f29b75bebd2dcfbd480024c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Duchesneau?= Date: Thu, 22 Sep 2022 11:16:23 -0400 Subject: [PATCH 023/253] add 'send_all_block_headers' param for blockHandler (#3971) * add 'send_all_block_headers' param for blockHandler * fix tests, add new trigger_every_block filter test --- chain/ethereum/src/adapter.rs | 76 +++++++++++++++++-- graph/proto/ethereum/transforms.proto | 25 +++++- .../src/firehose/sf.ethereum.transform.v1.rs | 23 +++++- 3 files changed, 113 insertions(+), 11 deletions(-) diff --git a/chain/ethereum/src/adapter.rs b/chain/ethereum/src/adapter.rs index 16b3035ae8a..6f78c15b834 100644 --- a/chain/ethereum/src/adapter.rs +++ b/chain/ethereum/src/adapter.rs @@ -167,21 +167,18 @@ impl bc::TriggerFilter for TriggerFilter { trigger_every_block, } = self.block.clone(); - if trigger_every_block { - return Vec::new(); - } - let log_filters: Vec = self.log.into(); let mut call_filters: Vec = self.call.into(); call_filters.extend(Into::>::into(self.block)); - if call_filters.is_empty() && log_filters.is_empty() { + if call_filters.is_empty() && log_filters.is_empty() && !trigger_every_block { return Vec::new(); } let combined_filter = CombinedFilter { log_filters, call_filters, + send_all_block_headers: trigger_every_block, }; vec![Any { @@ -1136,6 +1133,7 @@ mod tests { let CombinedFilter { log_filters: mut actual_log_filters, call_filters: mut actual_call_filters, + send_all_block_headers: actual_send_all_block_headers, } = combined_filter; actual_call_filters.sort_by(|a, b| a.addresses.cmp(&b.addresses)); @@ -1144,15 +1142,77 @@ mod tests { } assert_eq!(expected_call_filters, actual_call_filters); + actual_log_filters.sort_by(|a, b| a.addresses.cmp(&b.addresses)); + for filter in actual_log_filters.iter_mut() { + filter.event_signatures.sort(); + } + assert_eq!(expected_log_filters, actual_log_filters); + assert_eq!(false, actual_send_all_block_headers); + } + + #[test] + fn ethereum_trigger_filter_to_firehose_every_block_plus_logfilter() { + let address = Address::from_low_u64_be; + let sig = H256::from_low_u64_le; + let mut filter = TriggerFilter { + log: EthereumLogFilter { + contracts_and_events_graph: GraphMap::new(), + wildcard_events: HashMap::new(), + }, + call: EthereumCallFilter { + contract_addresses_function_signatures: HashMap::new(), + wildcard_signatures: HashSet::new(), + }, + block: EthereumBlockFilter { + contract_addresses: HashSet::new(), + trigger_every_block: true, + }, + }; + + filter.log.contracts_and_events_graph.add_edge( + LogFilterNode::Contract(address(10)), + LogFilterNode::Event(sig(101)), + false, + ); + + let expected_log_filters = vec![LogFilter { + addresses: vec![address(10).to_fixed_bytes().to_vec()], + event_signatures: vec![sig(101).to_fixed_bytes().to_vec()], + }]; + + let firehose_filter = filter.clone().to_firehose_filter(); + assert_eq!(1, firehose_filter.len()); + + let firehose_filter: HashMap<_, _> = HashMap::from_iter::>( + firehose_filter + .into_iter() + .map(|any| (any.type_url.clone(), any)) + .collect_vec(), + ); + + let mut combined_filter = &firehose_filter + .get(COMBINED_FILTER_TYPE_URL.into()) + .expect("a CombinedFilter") + .value[..]; + + let combined_filter = + CombinedFilter::decode(&mut combined_filter).expect("combined filter to decode"); + + let CombinedFilter { + log_filters: mut actual_log_filters, + call_filters: actual_call_filters, + send_all_block_headers: actual_send_all_block_headers, + } = combined_filter; + + assert_eq!(0, actual_call_filters.len()); + actual_log_filters.sort_by(|a, b| a.addresses.cmp(&b.addresses)); for filter in actual_log_filters.iter_mut() { filter.event_signatures.sort(); } assert_eq!(expected_log_filters, actual_log_filters); - filter.block.trigger_every_block = true; - let firehose_filter = filter.to_firehose_filter(); - assert_eq!(firehose_filter.len(), 0); + assert_eq!(true, actual_send_all_block_headers); } #[test] diff --git a/graph/proto/ethereum/transforms.proto b/graph/proto/ethereum/transforms.proto index 57a3e0cb861..3d24fc9ac6f 100644 --- a/graph/proto/ethereum/transforms.proto +++ b/graph/proto/ethereum/transforms.proto @@ -1,12 +1,33 @@ syntax = "proto3"; package sf.ethereum.transform.v1; -option go_package = "github.com/streamingfast/sf-ethereum/types/pb/sf/ethereum/transform/v1;pbtransform"; +option go_package = "github.com/streamingfast/firehose-ethereum/types/pb/sf/ethereum/transform/v1;pbtransform"; -// Log and CallTo Filters, applied as 'inclusive OR' +// CombinedFilter is a combination of "LogFilters" and "CallToFilters" +// +// It transforms the requested stream in two ways: +// 1. STRIPPING +// The block data is stripped from all transactions that don't +// match any of the filters. +// +// 2. SKIPPING +// If an "block index" covers a range containing a +// block that does NOT match any of the filters, the block will be +// skipped altogether, UNLESS send_all_block_headers is enabled +// In that case, the block would still be sent, but without any +// transactionTrace +// +// The SKIPPING feature only applies to historical blocks, because +// the "block index" is always produced after the merged-blocks files +// are produced. Therefore, the "live" blocks are never filtered out. +// message CombinedFilter { repeated LogFilter log_filters = 1; repeated CallToFilter call_filters = 2; + + // Always send all blocks. if they don't match any log_filters or call_filters, + // all the transactions will be filtered out, sending only the header. + bool send_all_block_headers = 3; } // MultiLogFilter concatenates the results of each LogFilter (inclusive OR) diff --git a/graph/src/firehose/sf.ethereum.transform.v1.rs b/graph/src/firehose/sf.ethereum.transform.v1.rs index 35784c20623..b677e1717c3 100644 --- a/graph/src/firehose/sf.ethereum.transform.v1.rs +++ b/graph/src/firehose/sf.ethereum.transform.v1.rs @@ -1,10 +1,31 @@ -/// Log and CallTo Filters, applied as 'inclusive OR' +/// CombinedFilter is a combination of "LogFilters" and "CallToFilters" +/// +/// It transforms the requested stream in two ways: +/// 1. STRIPPING +/// The block data is stripped from all transactions that don't +/// match any of the filters. +/// +/// 2. SKIPPING +/// If an "block index" covers a range containing a +/// block that does NOT match any of the filters, the block will be +/// skipped altogether, UNLESS send_all_block_headers is enabled +/// In that case, the block would still be sent, but without any +/// transactionTrace +/// +/// The SKIPPING feature only applies to historical blocks, because +/// the "block index" is always produced after the merged-blocks files +/// are produced. Therefore, the "live" blocks are never filtered out. +/// #[derive(Clone, PartialEq, ::prost::Message)] pub struct CombinedFilter { #[prost(message, repeated, tag="1")] pub log_filters: ::prost::alloc::vec::Vec, #[prost(message, repeated, tag="2")] pub call_filters: ::prost::alloc::vec::Vec, + /// Always send all blocks. if they don't match any log_filters or call_filters, + /// all the transactions will be filtered out, sending only the header. + #[prost(bool, tag="3")] + pub send_all_block_headers: bool, } /// MultiLogFilter concatenates the results of each LogFilter (inclusive OR) #[derive(Clone, PartialEq, ::prost::Message)] From fb8a94b578c5f6084aa28e2c621ea358474cea86 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Thu, 22 Sep 2022 16:18:23 +0100 Subject: [PATCH 024/253] add stopwatch (#3970) --- core/src/subgraph/instance_manager.rs | 1 + core/src/subgraph/trigger_processor.rs | 20 ++++++++++++-------- graph/src/components/metrics/subgraph.rs | 10 +++++++++- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/core/src/subgraph/instance_manager.rs b/core/src/subgraph/instance_manager.rs index 6085c4841e7..785f848df3d 100644 --- a/core/src/subgraph/instance_manager.rs +++ b/core/src/subgraph/instance_manager.rs @@ -285,6 +285,7 @@ impl SubgraphInstanceManager { let subgraph_metrics = Arc::new(SubgraphInstanceMetrics::new( registry.cheap_clone(), deployment.hash.as_str(), + stopwatch_metrics.clone(), )); let subgraph_metrics_unregister = subgraph_metrics.clone(); let host_metrics = Arc::new(HostMetrics::new( diff --git a/core/src/subgraph/trigger_processor.rs b/core/src/subgraph/trigger_processor.rs index 58e6c4a8806..6122faa99bc 100644 --- a/core/src/subgraph/trigger_processor.rs +++ b/core/src/subgraph/trigger_processor.rs @@ -35,16 +35,20 @@ where let mut host_mapping: Vec<(&T::Host, TriggerWithHandler>)> = vec![]; - for host in hosts { - let mapping_trigger = match host.match_and_decode(trigger, block, logger)? { - // Trigger matches and was decoded as a mapping trigger. - Some(mapping_trigger) => mapping_trigger, + { + let _section = subgraph_metrics.stopwatch.start_section("match_and_decode"); - // Trigger does not match, do not process it. - None => continue, - }; + for host in hosts { + let mapping_trigger = match host.match_and_decode(trigger, block, logger)? { + // Trigger matches and was decoded as a mapping trigger. + Some(mapping_trigger) => mapping_trigger, - host_mapping.push((&host, mapping_trigger)); + // Trigger does not match, do not process it. + None => continue, + }; + + host_mapping.push((&host, mapping_trigger)); + } } if host_mapping.is_empty() { diff --git a/graph/src/components/metrics/subgraph.rs b/graph/src/components/metrics/subgraph.rs index c274b934aad..5fff2f3dfc9 100644 --- a/graph/src/components/metrics/subgraph.rs +++ b/graph/src/components/metrics/subgraph.rs @@ -3,16 +3,23 @@ use crate::prelude::{Gauge, Histogram, HostMetrics, MetricsRegistry}; use std::collections::HashMap; use std::sync::Arc; +use super::stopwatch::StopwatchMetrics; + pub struct SubgraphInstanceMetrics { pub block_trigger_count: Box, pub block_processing_duration: Box, pub block_ops_transaction_duration: Box, + pub stopwatch: StopwatchMetrics, trigger_processing_duration: Box, } impl SubgraphInstanceMetrics { - pub fn new(registry: Arc, subgraph_hash: &str) -> Self { + pub fn new( + registry: Arc, + subgraph_hash: &str, + stopwatch: StopwatchMetrics, + ) -> Self { let block_trigger_count = registry .new_deployment_histogram( "deployment_block_trigger_count", @@ -51,6 +58,7 @@ impl SubgraphInstanceMetrics { block_processing_duration, trigger_processing_duration, block_ops_transaction_duration, + stopwatch, } } From 8ce2d61f8f48fb17fd9d3411de7bc74ab47df7fa Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Fri, 23 Sep 2022 00:22:47 +0200 Subject: [PATCH 025/253] docker: ship envsubst with the image (#3974) * docker: ship envsubst with the image * docs: variable expansion in config.md --- docker/Dockerfile | 9 +++++++++ docs/config.md | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 16a38350702..d15c8f4f808 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,6 +4,14 @@ # by running something like the following # docker build --target STAGE -f docker/Dockerfile . +FROM golang:buster as envsubst + +# v1.2.0 +ARG ENVSUBST_COMMIT_SHA=16035fe3571ad42c7796bf554f978bb2df64231b + +RUN go install github.com/a8m/envsubst/cmd/envsubst@$ENVSUBST_COMMIT_SHA \ + && strip -g /go/bin/envsubst + FROM rust:buster as graph-node-build ARG COMMIT_SHA=unknown @@ -88,5 +96,6 @@ RUN apt-get update \ ADD docker/wait_for docker/start /usr/local/bin/ COPY --from=graph-node-build /usr/local/cargo/bin/graph-node /usr/local/cargo/bin/graphman /usr/local/bin/ COPY --from=graph-node-build /etc/image-info /etc/image-info +COPY --from=envsubst /go/bin/envsubst /usr/local/bin/ COPY docker/Dockerfile /Dockerfile CMD start diff --git a/docs/config.md b/docs/config.md index 8b65e2511a3..8020334918c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -11,6 +11,11 @@ The TOML file consists of four sections: * `[ingestor]` sets the name of the node responsible for block ingestion. * `[deployment]` describes how to place newly deployed subgraphs. +Some of these sections support environment variable expansion out of the box, +most notably Postgres connection strings. The official `graph-node` Docker image +includes [`envsubst`](https://github.com/a8m/envsubst) for more complex use +cases. + ## Configuring Multiple Databases For most use cases, a single Postgres database is sufficient to support a From 2cfb46629240356eb3ff342c076594f6008e1258 Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Fri, 23 Sep 2022 12:53:47 +0200 Subject: [PATCH 026/253] all: macro sugar: `#[derive(Clone)]` and `thiserror`'s `#[from]` (#3983) * all: derive some Clone impls * all: use thiserror's #[this] feature --- chain/ethereum/src/adapter.rs | 8 +--- chain/ethereum/src/trigger.rs | 52 +------------------------- core/src/subgraph/error.rs | 8 +--- graph/src/blockchain/mock.rs | 7 +--- graph/src/blockchain/mod.rs | 8 +--- graph/src/data/query/error.rs | 8 +--- graph/src/data/subgraph/api_version.rs | 10 +---- graph/src/data/subgraph/mod.rs | 36 +++--------------- graph/src/runtime/mod.rs | 8 +--- mock/src/metrics_registry.rs | 7 +--- runtime/wasm/src/asc_abi/class.rs | 10 +---- server/http/src/server.rs | 8 +--- server/index-node/src/server.rs | 8 +--- server/metrics/src/lib.rs | 18 +-------- 14 files changed, 21 insertions(+), 175 deletions(-) diff --git a/chain/ethereum/src/adapter.rs b/chain/ethereum/src/adapter.rs index 6f78c15b834..18d585400e6 100644 --- a/chain/ethereum/src/adapter.rs +++ b/chain/ethereum/src/adapter.rs @@ -44,7 +44,7 @@ pub struct EthereumContractCall { #[derive(Error, Debug)] pub enum EthereumContractCallError { #[error("ABI error: {0}")] - ABIError(ABIError), + ABIError(#[from] ABIError), /// `Token` is not of expected `ParamType` #[error("type mismatch, token {0:?} is not of kind {1:?}")] TypeError(Token, ParamType), @@ -58,12 +58,6 @@ pub enum EthereumContractCallError { Timeout, } -impl From for EthereumContractCallError { - fn from(e: ABIError) -> Self { - EthereumContractCallError::ABIError(e) - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] enum LogFilterNode { Contract(Address), diff --git a/chain/ethereum/src/trigger.rs b/chain/ethereum/src/trigger.rs index 6aaae1a65b3..9d3a4a87855 100644 --- a/chain/ethereum/src/trigger.rs +++ b/chain/ethereum/src/trigger.rs @@ -410,7 +410,7 @@ impl From<&'_ Transaction> for EthereumTransactionData { } /// An Ethereum event logged from a specific contract address and block. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EthereumEventData { pub address: Address, pub log_index: U256, @@ -421,29 +421,8 @@ pub struct EthereumEventData { pub params: Vec, } -impl Clone for EthereumEventData { - fn clone(&self) -> Self { - EthereumEventData { - address: self.address, - log_index: self.log_index, - transaction_log_index: self.transaction_log_index, - log_type: self.log_type.clone(), - block: self.block.clone(), - transaction: self.transaction.clone(), - params: self - .params - .iter() - .map(|log_param| LogParam { - name: log_param.name.clone(), - value: log_param.value.clone(), - }) - .collect(), - } - } -} - /// An Ethereum call executed within a transaction within a block to a contract address. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EthereumCallData { pub from: Address, pub to: Address, @@ -452,30 +431,3 @@ pub struct EthereumCallData { pub inputs: Vec, pub outputs: Vec, } - -impl Clone for EthereumCallData { - fn clone(&self) -> Self { - EthereumCallData { - to: self.to, - from: self.from, - block: self.block.clone(), - transaction: self.transaction.clone(), - inputs: self - .inputs - .iter() - .map(|log_param| LogParam { - name: log_param.name.clone(), - value: log_param.value.clone(), - }) - .collect(), - outputs: self - .outputs - .iter() - .map(|log_param| LogParam { - name: log_param.name.clone(), - value: log_param.value.clone(), - }) - .collect(), - } - } -} diff --git a/core/src/subgraph/error.rs b/core/src/subgraph/error.rs index a8528fd81f7..b3131255aed 100644 --- a/core/src/subgraph/error.rs +++ b/core/src/subgraph/error.rs @@ -4,7 +4,7 @@ use graph::prelude::{thiserror, Error, StoreError}; #[derive(thiserror::Error, Debug)] pub enum BlockProcessingError { #[error("{0:#}")] - Unknown(Error), + Unknown(#[from] Error), // The error had a deterministic cause but, for a possibly non-deterministic reason, we chose to // halt processing due to the error. @@ -21,12 +21,6 @@ impl BlockProcessingError { } } -impl From for BlockProcessingError { - fn from(e: Error) -> Self { - BlockProcessingError::Unknown(e) - } -} - impl From for BlockProcessingError { fn from(e: StoreError) -> Self { BlockProcessingError::Unknown(e.into()) diff --git a/graph/src/blockchain/mock.rs b/graph/src/blockchain/mock.rs index ba638d82e71..5902795b45e 100644 --- a/graph/src/blockchain/mock.rs +++ b/graph/src/blockchain/mock.rs @@ -37,14 +37,9 @@ impl Block for MockBlock { } } +#[derive(Clone)] pub struct MockDataSource; -impl Clone for MockDataSource { - fn clone(&self) -> Self { - todo!() - } -} - impl TryFrom> for MockDataSource { type Error = Error; diff --git a/graph/src/blockchain/mod.rs b/graph/src/blockchain/mod.rs index 88df6170843..dc18103e05d 100644 --- a/graph/src/blockchain/mod.rs +++ b/graph/src/blockchain/mod.rs @@ -157,13 +157,7 @@ pub enum IngestorError { /// An unexpected error occurred. #[error("Ingestor error: {0:#}")] - Unknown(Error), -} - -impl From for IngestorError { - fn from(e: Error) -> Self { - IngestorError::Unknown(e) - } + Unknown(#[from] Error), } impl From for IngestorError { diff --git a/graph/src/data/query/error.rs b/graph/src/data/query/error.rs index 887005773b6..602162ddf23 100644 --- a/graph/src/data/query/error.rs +++ b/graph/src/data/query/error.rs @@ -12,15 +12,9 @@ use crate::data::subgraph::*; use crate::prelude::q; use crate::{components::store::StoreError, prelude::CacheWeight}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CloneableAnyhowError(Arc); -impl Clone for CloneableAnyhowError { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - impl From for CloneableAnyhowError { fn from(f: anyhow::Error) -> Self { Self(Arc::new(f)) diff --git a/graph/src/data/subgraph/api_version.rs b/graph/src/data/subgraph/api_version.rs index 7972d2727b4..79756399483 100644 --- a/graph/src/data/subgraph/api_version.rs +++ b/graph/src/data/subgraph/api_version.rs @@ -3,8 +3,6 @@ use semver::Version; use std::collections::BTreeSet; use thiserror::Error; -use super::SubgraphManifestValidationError; - pub const API_VERSION_0_0_2: Version = Version::new(0, 0, 2); /// This version adds a new subgraph validation step that rejects manifests whose mappings have @@ -75,13 +73,7 @@ pub(super) fn format_versions(versions: &BTreeSet) -> String { #[derive(Error, Debug, PartialEq)] #[error("Expected a single apiVersion for mappings. Found: {}.", format_versions(.0))] -pub struct DifferentMappingApiVersions(BTreeSet); - -impl From for SubgraphManifestValidationError { - fn from(versions: DifferentMappingApiVersions) -> Self { - SubgraphManifestValidationError::DifferentApiVersions(versions.0) - } -} +pub struct DifferentMappingApiVersions(pub BTreeSet); #[test] fn unified_mapping_api_version_from_iterator() { diff --git a/graph/src/data/subgraph/mod.rs b/graph/src/data/subgraph/mod.rs index 939c5bd3f65..3e451eb6a87 100644 --- a/graph/src/data/subgraph/mod.rs +++ b/graph/src/data/subgraph/mod.rs @@ -270,7 +270,7 @@ pub enum SubgraphRegistrarError { #[error("deployment assignment unchanged: {0}")] DeploymentAssignmentUnchanged(String), #[error("subgraph registrar internal query error: {0}")] - QueryExecutionError(QueryExecutionError), + QueryExecutionError(#[from] QueryExecutionError), #[error("subgraph registrar error with store: {0}")] StoreError(StoreError), #[error("subgraph validation error: {}", display_vector(.0))] @@ -278,13 +278,7 @@ pub enum SubgraphRegistrarError { #[error("subgraph deployment error: {0}")] SubgraphDeploymentError(StoreError), #[error("subgraph registrar error: {0}")] - Unknown(anyhow::Error), -} - -impl From for SubgraphRegistrarError { - fn from(e: QueryExecutionError) -> Self { - SubgraphRegistrarError::QueryExecutionError(e) - } + Unknown(#[from] anyhow::Error), } impl From for SubgraphRegistrarError { @@ -296,12 +290,6 @@ impl From for SubgraphRegistrarError { } } -impl From for SubgraphRegistrarError { - fn from(e: Error) -> Self { - SubgraphRegistrarError::Unknown(e) - } -} - impl From for SubgraphRegistrarError { fn from(e: SubgraphManifestValidationError) -> Self { SubgraphRegistrarError::ManifestValidationError(vec![e]) @@ -318,13 +306,7 @@ pub enum SubgraphAssignmentProviderError { #[error("Subgraph with ID {0} is not running")] NotRunning(DeploymentLocator), #[error("Subgraph provider error: {0}")] - Unknown(anyhow::Error), -} - -impl From for SubgraphAssignmentProviderError { - fn from(e: Error) -> Self { - SubgraphAssignmentProviderError::Unknown(e) - } + Unknown(#[from] anyhow::Error), } impl From<::diesel::result::Error> for SubgraphAssignmentProviderError { @@ -357,8 +339,8 @@ pub enum SubgraphManifestValidationError { SchemaValidationError(Vec), #[error("the graft base is invalid: {0}")] GraftBaseInvalid(String), - #[error("subgraph must use a single apiVersion across its data sources. Found: {}", format_versions(.0))] - DifferentApiVersions(BTreeSet), + #[error("subgraph must use a single apiVersion across its data sources. Found: {}", format_versions(&(.0).0))] + DifferentApiVersions(#[from] DifferentMappingApiVersions), #[error(transparent)] FeatureValidationError(#[from] SubgraphFeatureValidationError), #[error("data source {0} is invalid: {1}")] @@ -368,7 +350,7 @@ pub enum SubgraphManifestValidationError { #[derive(Error, Debug)] pub enum SubgraphManifestResolveError { #[error("parse error: {0}")] - ParseError(serde_yaml::Error), + ParseError(#[from] serde_yaml::Error), #[error("subgraph is not UTF-8")] NonUtf8, #[error("subgraph is not valid YAML")] @@ -377,12 +359,6 @@ pub enum SubgraphManifestResolveError { ResolveError(anyhow::Error), } -impl From for SubgraphManifestResolveError { - fn from(e: serde_yaml::Error) -> Self { - SubgraphManifestResolveError::ParseError(e) - } -} - /// Data source contexts are conveniently represented as entities. pub type DataSourceContext = Entity; diff --git a/graph/src/runtime/mod.rs b/graph/src/runtime/mod.rs index f86945f4bbf..14128e6a0b1 100644 --- a/graph/src/runtime/mod.rs +++ b/graph/src/runtime/mod.rs @@ -426,7 +426,7 @@ impl std::error::Error for DeterministicHostError {} #[derive(thiserror::Error, Debug)] pub enum HostExportError { #[error("{0:#}")] - Unknown(anyhow::Error), + Unknown(#[from] anyhow::Error), #[error("{0:#}")] PossibleReorg(anyhow::Error), @@ -435,12 +435,6 @@ pub enum HostExportError { Deterministic(anyhow::Error), } -impl From for HostExportError { - fn from(e: anyhow::Error) -> Self { - HostExportError::Unknown(e) - } -} - impl From for HostExportError { fn from(value: DeterministicHostError) -> Self { match value { diff --git a/mock/src/metrics_registry.rs b/mock/src/metrics_registry.rs index e7e238d061a..0b450523b64 100644 --- a/mock/src/metrics_registry.rs +++ b/mock/src/metrics_registry.rs @@ -4,6 +4,7 @@ use graph::prometheus::{CounterVec, GaugeVec, HistogramOpts, HistogramVec}; use std::collections::HashMap; +#[derive(Clone)] pub struct MockMetricsRegistry {} impl MockMetricsRegistry { @@ -12,12 +13,6 @@ impl MockMetricsRegistry { } } -impl Clone for MockMetricsRegistry { - fn clone(&self) -> Self { - Self {} - } -} - impl MetricsRegistryTrait for MockMetricsRegistry { fn register(&self, _name: &str, _c: Box) { // Ignore, we do not register metrics diff --git a/runtime/wasm/src/asc_abi/class.rs b/runtime/wasm/src/asc_abi/class.rs index 77ec457f127..5298eee76cb 100644 --- a/runtime/wasm/src/asc_abi/class.rs +++ b/runtime/wasm/src/asc_abi/class.rs @@ -705,7 +705,7 @@ impl AscIndexId for AscResult>, bool> { } #[repr(C)] -#[derive(AscType)] +#[derive(AscType, Copy, Clone)] pub struct AscWrapped { pub inner: V, } @@ -721,11 +721,3 @@ impl AscIndexId for AscWrapped { impl AscIndexId for AscWrapped>> { const INDEX_ASC_TYPE_ID: IndexForAscTypeId = IndexForAscTypeId::WrappedJsonValue; } - -impl Copy for AscWrapped {} - -impl Clone for AscWrapped { - fn clone(&self) -> Self { - Self { inner: self.inner } - } -} diff --git a/server/http/src/server.rs b/server/http/src/server.rs index e605f798c2c..a99e8bafe05 100644 --- a/server/http/src/server.rs +++ b/server/http/src/server.rs @@ -11,13 +11,7 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum GraphQLServeError { #[error("Bind error: {0}")] - BindError(hyper::Error), -} - -impl From for GraphQLServeError { - fn from(err: hyper::Error) -> Self { - GraphQLServeError::BindError(err) - } + BindError(#[from] hyper::Error), } /// A GraphQL server based on Hyper. diff --git a/server/index-node/src/server.rs b/server/index-node/src/server.rs index 2fdc6ddbb4d..ec65deead34 100644 --- a/server/index-node/src/server.rs +++ b/server/index-node/src/server.rs @@ -16,13 +16,7 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum IndexNodeServeError { #[error("Bind error: {0}")] - BindError(hyper::Error), -} - -impl From for IndexNodeServeError { - fn from(err: hyper::Error) -> Self { - IndexNodeServeError::BindError(err) - } + BindError(#[from] hyper::Error), } /// A GraphQL server based on Hyper. diff --git a/server/metrics/src/lib.rs b/server/metrics/src/lib.rs index 0109b862810..71d27b866a8 100644 --- a/server/metrics/src/lib.rs +++ b/server/metrics/src/lib.rs @@ -14,29 +14,15 @@ use graph::prelude::{MetricsServer as MetricsServerTrait, *}; #[derive(Debug, Error)] pub enum PrometheusMetricsServeError { #[error("Bind error: {0}")] - BindError(hyper::Error), -} - -impl From for PrometheusMetricsServeError { - fn from(err: hyper::Error) -> Self { - PrometheusMetricsServeError::BindError(err) - } + BindError(#[from] hyper::Error), } +#[derive(Clone)] pub struct PrometheusMetricsServer { logger: Logger, registry: Arc, } -impl Clone for PrometheusMetricsServer { - fn clone(&self) -> Self { - Self { - logger: self.logger.clone(), - registry: self.registry.clone(), - } - } -} - impl PrometheusMetricsServer { pub fn new(logger_factory: &LoggerFactory, registry: Arc) -> Self { PrometheusMetricsServer { From 178468a92da2c3332450b46a86187615db814178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Guimar=C3=A3es?= Date: Fri, 23 Sep 2022 09:48:44 -0300 Subject: [PATCH 027/253] graph,node: Migrate from structopt to clap v3 (#3982) * graph,node: Migrate from structopt to clap v3 Structopt is in maintenance mode and the authors suggest that users migrate to clap, which now has derive macros. * graph/examples: Replace structopt with clap Co-authored-by: tilacog --- Cargo.lock | 113 ++++++++---------------------------- graph/Cargo.toml | 2 +- graph/examples/stress.rs | 22 +++---- node/Cargo.toml | 3 +- node/src/bin/manager.rs | 118 ++++++++++++++++++++------------------ node/src/main.rs | 4 +- node/src/opt.rs | 62 ++++++++++---------- store/postgres/Cargo.toml | 2 +- 8 files changed, 132 insertions(+), 194 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1997353802..2b2b50d497f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,15 +54,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" version = "1.0.65" @@ -494,33 +485,32 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ - "ansi_term", "atty", "bitflags", - "strsim 0.8.0", - "term_size", - "textwrap 0.11.0", - "unicode-width", - "vec_map", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", ] [[package]] -name = "clap" -version = "3.2.21" +name = "clap_derive" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ - "atty", - "bitflags", - "clap_lex", - "indexmap", - "strsim 0.10.0", - "termcolor", - "textwrap 0.15.0", + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -886,7 +876,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim", "syn", ] @@ -1494,6 +1484,7 @@ dependencies = [ "bytes", "chrono", "cid", + "clap", "diesel", "diesel_derives", "envconfig", @@ -1530,7 +1521,6 @@ dependencies = [ "slog-term", "stable-hash 0.3.3", "stable-hash 0.4.2", - "structopt", "strum", "strum_macros", "test-store", @@ -1722,7 +1712,7 @@ dependencies = [ name = "graph-node" version = "0.27.0" dependencies = [ - "clap 3.2.21", + "clap", "crossbeam-channel", "diesel", "env_logger 0.9.0", @@ -1752,7 +1742,6 @@ dependencies = [ "serde", "serde_regex", "shellexpand", - "structopt", "toml", "url", ] @@ -1887,7 +1876,7 @@ dependencies = [ "anyhow", "async-trait", "blake3 1.3.1", - "clap 3.2.21", + "clap", "derive_more", "diesel", "diesel-derive-enum", @@ -4069,42 +4058,12 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "strum" version = "0.21.0" @@ -4201,16 +4160,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "term_size" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "termcolor" version = "1.1.2" @@ -4248,19 +4197,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "term_size", - "unicode-width", -] - -[[package]] -name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" @@ -4857,12 +4796,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.3" diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 89a82fa1ad4..bc3e1011cc1 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -66,8 +66,8 @@ serde_plain = "1.0.0" [dev-dependencies] test-store = { path = "../store/test-store" } +clap = { version = "3.2.22", features = ["derive", "env"] } maplit = "1.0.2" -structopt = { version = "0.3" } [build-dependencies] tonic-build = { version = "0.7.2", features = ["prost","compression"] } diff --git a/graph/examples/stress.rs b/graph/examples/stress.rs index 9414bd616db..0475437cecc 100644 --- a/graph/examples/stress.rs +++ b/graph/examples/stress.rs @@ -5,12 +5,12 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::Arc; use std::time::{Duration, Instant}; +use clap::Parser; use graph::data::value::{Object, Word}; use graph::object; use graph::prelude::{lazy_static, q, r, BigDecimal, BigInt, QueryResult}; use rand::SeedableRng; use rand::{rngs::SmallRng, Rng}; -use structopt::StructOpt; use graph::util::cache_weight::CacheWeight; use graph::util::lfu_cache::LfuCache; @@ -523,32 +523,32 @@ impl From for Entry { } // Command line arguments -#[derive(StructOpt)] -#[structopt(name = "stress", about = "Stress test for the LFU Cache")] +#[derive(Parser)] +#[clap(name = "stress", about = "Stress test for the LFU Cache")] struct Opt { /// Number of cache evictions and insertions - #[structopt(short, long, default_value = "1000")] + #[clap(short, long, default_value = "1000")] niter: usize, /// Print this many intermediate messages - #[structopt(short, long, default_value = "10")] + #[clap(short, long, default_value = "10")] print_count: usize, /// Use objects of size 0 up to this size, chosen unifromly randomly /// unless `--fixed` is given - #[structopt(short, long, default_value = "1024")] + #[clap(short, long, default_value = "1024")] obj_size: usize, - #[structopt(short, long, default_value = "1000000")] + #[clap(short, long, default_value = "1000000")] cache_size: usize, - #[structopt(short, long, default_value = "vec")] + #[clap(short, long, default_value = "vec")] template: String, - #[structopt(short, long)] + #[clap(short, long)] samples: bool, /// Always use objects of size `--obj-size` - #[structopt(short, long)] + #[clap(short, long)] fixed: bool, /// The seed of the random number generator. A seed of 0 means that all /// samples are taken from the same template object, and only differ in /// size - #[structopt(long)] + #[clap(long)] seed: Option, } diff --git a/node/Cargo.toml b/node/Cargo.toml index b6648c9a7f1..59ec4960ac9 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -13,7 +13,7 @@ name = "graphman" path = "src/bin/manager.rs" [dependencies] -clap = "3.2.21" +clap = { version = "3.2.22", features = ["derive", "env"] } env_logger = "0.9.0" git-testament = "0.2" graphql-parser = "0.4.0" @@ -39,7 +39,6 @@ graph-store-postgres = { path = "../store/postgres" } regex = "1.5.4" serde = { version = "1.0.126", features = ["derive", "rc"] } serde_regex = "1.1.0" -structopt = { version = "0.3.26", features = ["wrap_help"] } toml = "0.5.7" shellexpand = "2.1.0" diesel = "1.4.8" diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index 1e8da9dd8e2..9c8e9aa708f 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -1,3 +1,4 @@ +use clap::{Parser, Subcommand}; use config::PoolSize; use git_testament::{git_testament, render_testament}; use graph::{data::graphql::effort::LoadManager, prelude::chrono, prometheus::Registry}; @@ -27,8 +28,6 @@ use graph_store_postgres::{ }; use lazy_static::lazy_static; use std::{collections::HashMap, env, num::ParseIntError, sync::Arc, time::Duration}; -use structopt::StructOpt; - const VERSION_LABEL_KEY: &str = "version"; git_testament!(TESTAMENT); @@ -37,22 +36,22 @@ lazy_static! { static ref RENDERED_TESTAMENT: String = render_testament!(TESTAMENT); } -#[derive(Clone, Debug, StructOpt)] -#[structopt( +#[derive(Parser, Clone, Debug)] +#[clap( name = "graphman", about = "Management tool for a graph-node infrastructure", author = "Graph Protocol, Inc.", version = RENDERED_TESTAMENT.as_str() )] pub struct Opt { - #[structopt( + #[clap( long, short, env = "GRAPH_NODE_CONFIG", help = "the name of the configuration file\n" )] pub config: String, - #[structopt( + #[clap( long, default_value = "default", value_name = "NODE_ID", @@ -60,7 +59,7 @@ pub struct Opt { help = "a unique identifier for this node.\nShould have the same value between consecutive node restarts\n" )] pub node_id: String, - #[structopt( + #[clap( long, value_name = "{HOST:PORT|URL}", default_value = "https://api.thegraph.com/ipfs/", @@ -68,25 +67,25 @@ pub struct Opt { help = "HTTP addresses of IPFS nodes" )] pub ipfs: Vec, - #[structopt( + #[clap( long, default_value = "3", help = "the size for connection pools. Set to 0\n to use pool size from configuration file\n corresponding to NODE_ID" )] pub pool_size: u32, - #[structopt(long, value_name = "URL", help = "Base URL for forking subgraphs")] + #[clap(long, value_name = "URL", help = "Base URL for forking subgraphs")] pub fork_base: Option, - #[structopt(long, help = "version label, used for prometheus metrics")] + #[clap(long, help = "version label, used for prometheus metrics")] pub version_label: Option, - #[structopt(subcommand)] + #[clap(subcommand)] pub cmd: Command, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum Command { /// Calculate the transaction speed TxnSpeed { - #[structopt(long, short, default_value = "60")] + #[clap(long, short, default_value = "60")] delay: u64, }, /// Print details about a deployment @@ -99,22 +98,23 @@ pub enum Command { /// The deployment (see above) deployment: DeploymentSearch, /// List only current version - #[structopt(long, short)] + #[clap(long, short)] current: bool, /// List only pending versions - #[structopt(long, short)] + #[clap(long, short)] pending: bool, /// Include status information - #[structopt(long, short)] + #[clap(long, short)] status: bool, /// List only used (current and pending) versions - #[structopt(long, short)] + #[clap(long, short)] used: bool, }, /// Manage unused deployments /// /// Record which deployments are unused with `record`, then remove them /// with `remove` + #[clap(flatten)] Unused(UnusedCommand), /// Remove a named subgraph Remove { @@ -142,10 +142,10 @@ pub enum Command { Rewind { /// Force rewinding even if the block hash is not found in the local /// database - #[structopt(long, short)] + #[clap(long, short)] force: bool, /// Sleep for this many seconds after pausing subgraphs - #[structopt( + #[clap( long, short, default_value = "10", @@ -179,18 +179,21 @@ pub enum Command { /// /// Print information about a configuration file without /// actually connecting to databases or network clients + #[clap(flatten)] Config(ConfigCommand), /// Listen for store events and print them + #[clap(flatten)] Listen(ListenCommand), /// Manage deployment copies and grafts + #[clap(flatten)] Copy(CopyCommand), /// Run a GraphQL query Query { /// Save the JSON query result in this file - #[structopt(long, short)] + #[clap(long, short)] output: Option, /// Save the query trace in this file - #[structopt(long, short)] + #[clap(long, short)] trace: Option, /// The subgraph to query @@ -203,11 +206,14 @@ pub enum Command { vars: Vec, }, /// Get information about chains and manipulate them + #[clap(flatten)] Chain(ChainCommand), /// Manipulate internal subgraph statistics + #[clap(flatten)] Stats(StatsCommand), /// Manage database indexes + #[clap(flatten)] Index(IndexCommand), /// Prune deployments @@ -215,10 +221,10 @@ pub enum Command { /// The deployment to prune (see `help info`) deployment: DeploymentSearch, /// Prune tables with a ratio of entities to entity versions lower than this - #[structopt(long, short, default_value = "0.20")] + #[clap(long, short, default_value = "0.20")] prune_ratio: f64, /// How much history to keep in blocks - #[structopt(long, short, default_value = "10000")] + #[clap(long, short, default_value = "10000")] history: usize, }, } @@ -232,12 +238,12 @@ impl Command { } } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum UnusedCommand { /// List unused deployments List { /// Only list unused deployments that still exist - #[structopt(short, long)] + #[clap(short, long)] existing: bool, }, /// Update and record currently unused deployments @@ -248,23 +254,23 @@ pub enum UnusedCommand { /// i.e., smaller deployments are removed before larger ones Remove { /// How many unused deployments to remove (default: all) - #[structopt(short, long)] + #[clap(short, long)] count: Option, /// Remove a specific deployment - #[structopt(short, long, conflicts_with = "count")] + #[clap(short, long, conflicts_with = "count")] deployment: Option, /// Remove unused deployments that were recorded at least this many minutes ago - #[structopt(short, long)] + #[clap(short, long)] older: Option, }, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum ConfigCommand { /// Check and validate the configuration file Check { /// Print the configuration as JSON - #[structopt(long)] + #[clap(long)] print: bool, }, /// Print how a specific subgraph would be placed @@ -279,7 +285,7 @@ pub enum ConfigCommand { /// The names of the nodes that are going to run nodes: Vec, /// Print connections by shard rather than by node - #[structopt(short, long)] + #[clap(short, long)] shard: bool, }, /// Show eligible providers @@ -288,13 +294,13 @@ pub enum ConfigCommand { /// network with the given features. Set the name of the node for which /// to simulate placement with the toplevel `--node-id` option Provider { - #[structopt(short, long, default_value = "")] + #[clap(short, long, default_value = "")] features: String, network: String, }, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum ListenCommand { /// Listen only to assignment events Assignments, @@ -306,7 +312,7 @@ pub enum ListenCommand { entity_types: Vec, }, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum CopyCommand { /// Create a copy of an existing subgraph /// @@ -318,7 +324,7 @@ pub enum CopyCommand { /// should be chosen such that only final blocks are copied Create { /// How far behind `src` subgraph head to copy - #[structopt(long, short, default_value = "200")] + #[clap(long, short, default_value = "200")] offset: u32, /// The source deployment (see `help info`) src: DeploymentSearch, @@ -347,13 +353,13 @@ pub enum CopyCommand { }, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum ChainCommand { /// List all chains that are in the database List, /// Show information about a chain Info { - #[structopt( + #[clap( long, short, default_value = "50", @@ -361,7 +367,7 @@ pub enum ChainCommand { help = "the reorg threshold to check\n" )] reorg_threshold: i32, - #[structopt(long, help = "display block hashes\n")] + #[clap(long, help = "display block hashes\n")] hashes: bool, name: String, }, @@ -373,25 +379,25 @@ pub enum ChainCommand { /// Compares cached blocks with fresh ones and clears the block cache when they differ. CheckBlocks { - #[structopt(subcommand)] // Note that we mark a field as a subcommand + #[clap(subcommand)] // Note that we mark a field as a subcommand method: CheckBlockMethod, /// Chain name (must be an existing chain, see 'chain list') - #[structopt(empty_values = false)] + #[clap(empty_values = false)] chain_name: String, }, /// Truncates the whole block cache for the given chain. Truncate { /// Chain name (must be an existing chain, see 'chain list') - #[structopt(empty_values = false)] + #[clap(empty_values = false)] chain_name: String, /// Skips confirmation prompt - #[structopt(long, short)] + #[clap(long, short)] force: bool, }, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum StatsCommand { /// Toggle whether a table is account-like /// @@ -400,7 +406,7 @@ pub enum StatsCommand { /// to distinct entities. It can take up to 5 minutes for this to take /// effect. AccountLike { - #[structopt(long, short, help = "do not set but clear the account-like flag\n")] + #[clap(long, short, help = "do not set but clear the account-like flag\n")] clear: bool, /// The deployment (see `help info`). deployment: DeploymentSearch, @@ -425,7 +431,7 @@ pub enum StatsCommand { }, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum IndexCommand { /// Creates a new database index. /// @@ -438,22 +444,22 @@ pub enum IndexCommand { /// This command may be time-consuming. Create { /// The deployment (see `help info`). - #[structopt(empty_values = false)] + #[clap(empty_values = false)] deployment: DeploymentSearch, /// The Entity name. /// /// Can be expressed either in upper camel case (as its GraphQL definition) or in snake case /// (as its SQL table name). - #[structopt(empty_values = false)] + #[clap(empty_values = false)] entity: String, /// The Field names. /// /// Each field can be expressed either in camel case (as its GraphQL definition) or in snake /// case (as its SQL colmun name). - #[structopt(min_values = 1, required = true)] + #[clap(min_values = 1, required = true)] fields: Vec, /// The index method. Defaults to `btree`. - #[structopt( + #[clap( short, long, default_value = "btree", possible_values = &["btree", "hash", "gist", "spgist", "gin", "brin"] )] @@ -462,28 +468,28 @@ pub enum IndexCommand { /// Lists existing indexes for a given Entity List { /// The deployment (see `help info`). - #[structopt(empty_values = false)] + #[clap(empty_values = false)] deployment: DeploymentSearch, /// The Entity name. /// /// Can be expressed either in upper camel case (as its GraphQL definition) or in snake case /// (as its SQL table name). - #[structopt(empty_values = false)] + #[clap(empty_values = false)] entity: String, }, /// Drops an index for a given deployment, concurrently Drop { /// The deployment (see `help info`). - #[structopt(empty_values = false)] + #[clap(empty_values = false)] deployment: DeploymentSearch, /// The name of the index to be dropped - #[structopt(empty_values = false)] + #[clap(empty_values = false)] index_name: String, }, } -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Subcommand)] pub enum CheckBlockMethod { /// The number of the target block ByHash { hash: String }, @@ -493,9 +499,9 @@ pub enum CheckBlockMethod { /// A block number range, inclusive on both ends. ByRange { - #[structopt(long, short)] + #[clap(long, short)] from: Option, - #[structopt(long, short)] + #[clap(long, short)] to: Option, }, } diff --git a/node/src/main.rs b/node/src/main.rs index e8c867a2a69..326d5e8fbec 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -1,3 +1,4 @@ +use clap::Parser as _; use ethereum::chain::{EthereumAdapterSelector, EthereumStreamBuilder}; use ethereum::{ BlockIngestor as EthereumBlockIngestor, EthereumAdapterTrait, EthereumNetworks, RuntimeAdapter, @@ -44,7 +45,6 @@ use std::path::Path; use std::sync::atomic; use std::time::Duration; use std::{collections::HashMap, env}; -use structopt::StructOpt; use tokio::sync::mpsc; git_testament!(TESTAMENT); @@ -93,7 +93,7 @@ fn read_expensive_queries( async fn main() { env_logger::init(); - let opt = opt::Opt::from_args(); + let opt = opt::Opt::parse(); // Set up logger let logger = logger(opt.debug); diff --git a/node/src/opt.rs b/node/src/opt.rs index 785b73f517e..c40c1c50d2d 100644 --- a/node/src/opt.rs +++ b/node/src/opt.rs @@ -1,6 +1,6 @@ +use clap::Parser; use git_testament::{git_testament, render_testament}; use lazy_static::lazy_static; -use structopt::StructOpt; use crate::config; @@ -9,15 +9,15 @@ lazy_static! { static ref RENDERED_TESTAMENT: String = render_testament!(TESTAMENT); } -#[derive(Clone, Debug, StructOpt)] -#[structopt( +#[derive(Clone, Debug, Parser)] +#[clap( name = "graph-node", about = "Scalable queries for a decentralized future", author = "Graph Protocol, Inc.", version = RENDERED_TESTAMENT.as_str() )] pub struct Opt { - #[structopt( + #[clap( long, env = "GRAPH_NODE_CONFIG", conflicts_with_all = &["postgres-url", "postgres-secondary-hosts", "postgres-host-weights"], @@ -25,9 +25,9 @@ pub struct Opt { help = "the name of the configuration file", )] pub config: Option, - #[structopt(long, help = "validate the configuration and exit")] + #[clap(long, help = "validate the configuration and exit")] pub check_config: bool, - #[structopt( + #[clap( long, value_name = "[NAME:]IPFS_HASH", env = "SUBGRAPH", @@ -35,14 +35,14 @@ pub struct Opt { )] pub subgraph: Option, - #[structopt( + #[clap( long, value_name = "BLOCK_HASH:BLOCK_NUMBER", help = "block hash and number that the subgraph passed will start indexing at" )] pub start_block: Option, - #[structopt( + #[clap( long, value_name = "URL", env = "POSTGRES_URL", @@ -51,7 +51,7 @@ pub struct Opt { help = "Location of the Postgres database used for storing entities" )] pub postgres_url: Option, - #[structopt( + #[clap( long, value_name = "URL,", use_delimiter = true, @@ -62,7 +62,7 @@ pub struct Opt { )] // FIXME: Make sure delimiter is ',' pub postgres_secondary_hosts: Vec, - #[structopt( + #[clap( long, value_name = "WEIGHT,", use_delimiter = true, @@ -74,7 +74,7 @@ pub struct Opt { Defaults to weight 1 for each host" )] pub postgres_host_weights: Vec, - #[structopt( + #[clap( long, min_values=0, required_unless_one = &["ethereum-ws", "ethereum-ipc", "config"], @@ -84,7 +84,7 @@ pub struct Opt { help= "Ethereum network name (e.g. 'mainnet'), optional comma-seperated capabilities (eg 'full,archive'), and an Ethereum RPC URL, separated by a ':'", )] pub ethereum_rpc: Vec, - #[structopt(long, min_values=0, + #[clap(long, min_values=0, required_unless_one = &["ethereum-rpc", "ethereum-ipc", "config"], conflicts_with_all = &["ethereum-rpc", "ethereum-ipc", "config"], value_name="NETWORK_NAME:[CAPABILITIES]:URL", @@ -92,7 +92,7 @@ pub struct Opt { help= "Ethereum network name (e.g. 'mainnet'), optional comma-seperated capabilities (eg 'full,archive`, and an Ethereum WebSocket URL, separated by a ':'", )] pub ethereum_ws: Vec, - #[structopt(long, min_values=0, + #[clap(long, min_values=0, required_unless_one = &["ethereum-rpc", "ethereum-ws", "config"], conflicts_with_all = &["ethereum-rpc", "ethereum-ws", "config"], value_name="NETWORK_NAME:[CAPABILITIES]:FILE", @@ -100,14 +100,14 @@ pub struct Opt { help= "Ethereum network name (e.g. 'mainnet'), optional comma-seperated capabilities (eg 'full,archive'), and an Ethereum IPC pipe, separated by a ':'", )] pub ethereum_ipc: Vec, - #[structopt( + #[clap( long, value_name = "HOST:PORT", env = "IPFS", help = "HTTP addresses of IPFS nodes" )] pub ipfs: Vec, - #[structopt( + #[clap( long, default_value = "8000", value_name = "PORT", @@ -115,14 +115,14 @@ pub struct Opt { env = "GRAPH_GRAPHQL_HTTP_PORT" )] pub http_port: u16, - #[structopt( + #[clap( long, default_value = "8030", value_name = "PORT", help = "Port for the index node server" )] pub index_node_port: u16, - #[structopt( + #[clap( long, default_value = "8001", value_name = "PORT", @@ -130,21 +130,21 @@ pub struct Opt { env = "GRAPH_GRAPHQL_WS_PORT" )] pub ws_port: u16, - #[structopt( + #[clap( long, default_value = "8020", value_name = "PORT", help = "Port for the JSON-RPC admin server" )] pub admin_port: u16, - #[structopt( + #[clap( long, default_value = "8040", value_name = "PORT", help = "Port for the Prometheus metrics server" )] pub metrics_port: u16, - #[structopt( + #[clap( long, default_value = "default", value_name = "NODE_ID", @@ -152,7 +152,7 @@ pub struct Opt { help = "a unique identifier for this node. Should have the same value between consecutive node restarts" )] pub node_id: String, - #[structopt( + #[clap( long, value_name = "FILE", env = "GRAPH_NODE_EXPENSIVE_QUERIES_FILE", @@ -160,24 +160,24 @@ pub struct Opt { help = "a file with a list of expensive queries, one query per line. Attempts to run these queries will return a QueryExecutionError::TooExpensive to clients" )] pub expensive_queries_filename: String, - #[structopt(long, help = "Enable debug logging")] + #[clap(long, help = "Enable debug logging")] pub debug: bool, - #[structopt( + #[clap( long, value_name = "URL", env = "ELASTICSEARCH_URL", help = "Elasticsearch service to write subgraph logs to" )] pub elasticsearch_url: Option, - #[structopt( + #[clap( long, value_name = "USER", env = "ELASTICSEARCH_USER", help = "User to use for Elasticsearch logging" )] pub elasticsearch_user: Option, - #[structopt( + #[clap( long, value_name = "PASSWORD", env = "ELASTICSEARCH_PASSWORD", @@ -185,7 +185,7 @@ pub struct Opt { help = "Password to use for Elasticsearch logging" )] pub elasticsearch_password: Option, - #[structopt( + #[clap( long, value_name = "MILLISECONDS", default_value = "1000", @@ -193,14 +193,14 @@ pub struct Opt { help = "How often to poll the Ethereum node for new blocks" )] pub ethereum_polling_interval: u64, - #[structopt( + #[clap( long, value_name = "DISABLE_BLOCK_INGESTOR", env = "DISABLE_BLOCK_INGESTOR", help = "Ensures that the block ingestor component does not execute" )] pub disable_block_ingestor: bool, - #[structopt( + #[clap( long, value_name = "STORE_CONNECTION_POOL_SIZE", default_value = "10", @@ -208,20 +208,20 @@ pub struct Opt { help = "Limits the number of connections in the store's connection pool" )] pub store_connection_pool_size: u32, - #[structopt( + #[clap( long, help = "Allows setting configurations that may result in incorrect Proofs of Indexing." )] pub unsafe_config: bool, - #[structopt( + #[clap( long, value_name = "IPFS_HASH", help = "IPFS hash of the subgraph manifest that you want to fork" )] pub debug_fork: Option, - #[structopt(long, value_name = "URL", help = "Base URL for forking subgraphs")] + #[clap(long, value_name = "URL", help = "Base URL for forking subgraphs")] pub fork_base: Option, } diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index 092b2cad2b5..f93a2d047e6 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -36,7 +36,7 @@ hex = "0.4.3" [dev-dependencies] futures = "0.3" -clap = "3.2.21" +clap = "3.2.22" graphql-parser = "0.4.0" test-store = { path = "../test-store" } hex-literal = "0.3" From 0ec2d03dd9846c843bb10b16bb37306f6f49a628 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Sep 2022 16:34:27 +0100 Subject: [PATCH 028/253] build(deps): bump iana-time-zone from 0.1.44 to 0.1.47 (#3892) Bumps [iana-time-zone](https://github.com/strawlab/iana-time-zone) from 0.1.44 to 0.1.47. - [Release notes](https://github.com/strawlab/iana-time-zone/releases) - [Changelog](https://github.com/strawlab/iana-time-zone/blob/main/CHANGELOG.md) - [Commits](https://github.com/strawlab/iana-time-zone/compare/0.1.44...v0.1.47) --- updated-dependencies: - dependency-name: iana-time-zone dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b2b50d497f..f2a18ec66ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,9 +47,9 @@ dependencies = [ [[package]] name = "android_system_properties" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] @@ -2172,13 +2172,14 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.44" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf7d67cf4a22adc5be66e75ebdf769b3f2ea032041437a7061f97a63dad4b" +checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7" dependencies = [ "android_system_properties", "core-foundation-sys", "js-sys", + "once_cell", "wasm-bindgen", "winapi", ] From 5ddb87b0f4904f97c55ca445a8bed665b21559d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Sep 2022 16:35:30 +0100 Subject: [PATCH 029/253] build(deps): bump prometheus from 0.13.1 to 0.13.2 (#3939) Bumps [prometheus](https://github.com/tikv/rust-prometheus) from 0.13.1 to 0.13.2. - [Release notes](https://github.com/tikv/rust-prometheus/releases) - [Changelog](https://github.com/tikv/rust-prometheus/blob/master/CHANGELOG.md) - [Commits](https://github.com/tikv/rust-prometheus/compare/v0.13.1...v0.13.2) --- updated-dependencies: - dependency-name: prometheus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- graph/Cargo.toml | 2 +- node/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2a18ec66ee..3a10677eed2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3189,9 +3189,9 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cface98dfa6d645ea4c789839f176e4b072265d085bfcc48eaa8d137f58d3c39" +checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" dependencies = [ "cfg-if 1.0.0", "fnv", diff --git a/graph/Cargo.toml b/graph/Cargo.toml index bc3e1011cc1..6062689cc86 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -47,7 +47,7 @@ tokio = { version = "1.16.1", features = ["time", "sync", "macros", "test-util", tokio-stream = { version = "0.1.9", features = ["sync"] } tokio-retry = "0.3.0" url = "2.2.1" -prometheus = "0.13.1" +prometheus = "0.13.2" priority-queue = "0.7.0" tonic = { version = "0.7.1", features = ["tls-roots","compression"] } prost = "0.10.4" diff --git a/node/Cargo.toml b/node/Cargo.toml index 59ec4960ac9..ddee868515b 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -43,5 +43,5 @@ toml = "0.5.7" shellexpand = "2.1.0" diesel = "1.4.8" http = "0.2.5" # must be compatible with the version rust-web3 uses -prometheus = { version ="0.13.1", features = ["push"] } +prometheus = { version ="0.13.2", features = ["push"] } json-structural-diff = {version = "0.1", features = ["colorize"] } From 058abf0eb28f3faa99e69cefcf7f1823c1ddde07 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 23 Sep 2022 10:41:01 -0700 Subject: [PATCH 030/253] store: Fix the query for retrieving replication lag The old query was returning just the millisecond portion of the lag, not the lag in milliseconds --- store/postgres/src/catalog.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 99e1fbb604d..4a0a8d043c6 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -629,7 +629,7 @@ pub(crate) fn replication_lag(conn: &PgConnection) -> Result(conn)?; From befb466ed513af87d53c697a1d0f1775c5bf273e Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 23 Sep 2022 12:39:41 -0700 Subject: [PATCH 031/253] node: Fix graphman subcommands --- node/src/bin/manager.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index 9c8e9aa708f..20834f63f77 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -114,7 +114,7 @@ pub enum Command { /// /// Record which deployments are unused with `record`, then remove them /// with `remove` - #[clap(flatten)] + #[clap(subcommand)] Unused(UnusedCommand), /// Remove a named subgraph Remove { @@ -179,13 +179,13 @@ pub enum Command { /// /// Print information about a configuration file without /// actually connecting to databases or network clients - #[clap(flatten)] + #[clap(subcommand)] Config(ConfigCommand), /// Listen for store events and print them - #[clap(flatten)] + #[clap(subcommand)] Listen(ListenCommand), /// Manage deployment copies and grafts - #[clap(flatten)] + #[clap(subcommand)] Copy(CopyCommand), /// Run a GraphQL query Query { @@ -206,14 +206,14 @@ pub enum Command { vars: Vec, }, /// Get information about chains and manipulate them - #[clap(flatten)] + #[clap(subcommand)] Chain(ChainCommand), /// Manipulate internal subgraph statistics - #[clap(flatten)] + #[clap(subcommand)] Stats(StatsCommand), /// Manage database indexes - #[clap(flatten)] + #[clap(subcommand)] Index(IndexCommand), /// Prune deployments @@ -726,7 +726,7 @@ impl Context { #[tokio::main] async fn main() -> anyhow::Result<()> { - let opt = Opt::from_args(); + let opt = Opt::parse(); let version_label = opt.version_label.clone(); // Set up logger From e5198128bd3d10c95325580ade063f658709fbe4 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 23 Sep 2022 12:43:53 -0700 Subject: [PATCH 032/253] node: Make 'run' docstring a bit more readable --- node/src/bin/manager.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index 20834f63f77..f68a2dbdfed 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -159,9 +159,14 @@ pub enum Command { /// The deployments to rewind (see `help info`) deployments: Vec, }, - /// Deploy and run an arbitrary subgraph up to a certain block, although it can surpass it by a few blocks, it's not exact (use for dev and testing purposes) -- WARNING: WILL RUN MIGRATIONS ON THE DB, DO NOT USE IN PRODUCTION + /// Deploy and run an arbitrary subgraph up to a certain block /// - /// Also worth noting that the deployed subgraph will be removed at the end. + /// The run can surpass it by a few blocks, it's not exact (use for dev + /// and testing purposes) -- WARNING: WILL RUN MIGRATIONS ON THE DB, DO + /// NOT USE IN PRODUCTION + /// + /// Also worth noting that the deployed subgraph will be removed at the + /// end. Run { /// Network name (must fit one of the chain) network_name: String, From 01fcd92b33e791dc727f60993dd2bb7272a756b2 Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Mon, 26 Sep 2022 13:05:25 +0200 Subject: [PATCH 033/253] all: release `0.28.0-rc.0` (#3961) * Release 0.28.0 * news: Release v0.28.0 --- Cargo.lock | 44 +++++++-------- NEWS.md | 102 ++++++++++++++++++++++++++++++++--- chain/arweave/Cargo.toml | 2 +- chain/common/Cargo.toml | 2 +- chain/cosmos/Cargo.toml | 2 +- chain/ethereum/Cargo.toml | 2 +- chain/near/Cargo.toml | 2 +- chain/substreams/Cargo.toml | 2 +- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- graphql/Cargo.toml | 2 +- mock/Cargo.toml | 2 +- node/Cargo.toml | 2 +- runtime/derive/Cargo.toml | 2 +- runtime/test/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- server/http/Cargo.toml | 2 +- server/index-node/Cargo.toml | 2 +- server/json-rpc/Cargo.toml | 2 +- server/metrics/Cargo.toml | 2 +- server/websocket/Cargo.toml | 2 +- store/postgres/Cargo.toml | 2 +- store/test-store/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- 24 files changed, 140 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a10677eed2..1d2db6054c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1473,7 +1473,7 @@ dependencies = [ [[package]] name = "graph" -version = "0.27.0" +version = "0.28.0" dependencies = [ "Inflector", "anyhow", @@ -1538,7 +1538,7 @@ dependencies = [ [[package]] name = "graph-chain-arweave" -version = "0.27.0" +version = "0.28.0" dependencies = [ "base64-url", "diesel", @@ -1554,7 +1554,7 @@ dependencies = [ [[package]] name = "graph-chain-common" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "heck 0.4.0", @@ -1564,7 +1564,7 @@ dependencies = [ [[package]] name = "graph-chain-cosmos" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "graph", @@ -1580,7 +1580,7 @@ dependencies = [ [[package]] name = "graph-chain-ethereum" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "base64", @@ -1606,7 +1606,7 @@ dependencies = [ [[package]] name = "graph-chain-near" -version = "0.27.0" +version = "0.28.0" dependencies = [ "base64", "diesel", @@ -1621,7 +1621,7 @@ dependencies = [ [[package]] name = "graph-chain-substreams" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "async-stream", @@ -1647,7 +1647,7 @@ dependencies = [ [[package]] name = "graph-core" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "async-stream", @@ -1681,7 +1681,7 @@ dependencies = [ [[package]] name = "graph-graphql" -version = "0.27.0" +version = "0.28.0" dependencies = [ "Inflector", "anyhow", @@ -1703,14 +1703,14 @@ dependencies = [ [[package]] name = "graph-mock" -version = "0.27.0" +version = "0.28.0" dependencies = [ "graph", ] [[package]] name = "graph-node" -version = "0.27.0" +version = "0.28.0" dependencies = [ "clap", "crossbeam-channel", @@ -1748,7 +1748,7 @@ dependencies = [ [[package]] name = "graph-runtime-derive" -version = "0.27.0" +version = "0.28.0" dependencies = [ "heck 0.4.0", "proc-macro2", @@ -1758,7 +1758,7 @@ dependencies = [ [[package]] name = "graph-runtime-test" -version = "0.27.0" +version = "0.28.0" dependencies = [ "graph", "graph-chain-ethereum", @@ -1774,7 +1774,7 @@ dependencies = [ [[package]] name = "graph-runtime-wasm" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "async-trait", @@ -1800,7 +1800,7 @@ dependencies = [ [[package]] name = "graph-server-http" -version = "0.27.0" +version = "0.28.0" dependencies = [ "futures 0.1.31", "graph", @@ -1814,7 +1814,7 @@ dependencies = [ [[package]] name = "graph-server-index-node" -version = "0.27.0" +version = "0.28.0" dependencies = [ "blake3 1.3.1", "either", @@ -1834,7 +1834,7 @@ dependencies = [ [[package]] name = "graph-server-json-rpc" -version = "0.27.0" +version = "0.28.0" dependencies = [ "graph", "jsonrpsee", @@ -1843,7 +1843,7 @@ dependencies = [ [[package]] name = "graph-server-metrics" -version = "0.27.0" +version = "0.28.0" dependencies = [ "graph", "http", @@ -1854,7 +1854,7 @@ dependencies = [ [[package]] name = "graph-server-websocket" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "futures 0.1.31", @@ -1870,7 +1870,7 @@ dependencies = [ [[package]] name = "graph-store-postgres" -version = "0.27.0" +version = "0.28.0" dependencies = [ "Inflector", "anyhow", @@ -1910,7 +1910,7 @@ dependencies = [ [[package]] name = "graph-tests" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "async-stream", @@ -4182,7 +4182,7 @@ dependencies = [ [[package]] name = "test-store" -version = "0.27.0" +version = "0.28.0" dependencies = [ "diesel", "graph", diff --git a/NEWS.md b/NEWS.md index 497c56de70c..e526944dd96 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,13 +2,103 @@ ## Unreleased -### New DB table for dynamic data sources +## v0.28.0 + +#### Upgrade notes + +- **New DB table for dynamic data sources.** + For new subgraph deployments, dynamic data sources will be recorded under the `sgd*.data_sources$` table, rather than `subgraphs.dynamic_ethereum_contract_data_source`. As a consequence new deployments will not work correctly on earlier graph node versions, so _downgrading to an earlier graph node version is not supported_. + See issue [#3405](https://github.com/graphprotocol/graph-node/issues/3405) for other details. + +### What's new + +- The filepath which "too expensive qeueries" are sourced from is now configurable. You can use either the `GRAPH_NODE_EXPENSIVE_QUERIES_FILE` environment variable or the `expensive_queries_filename` option in the TOML configuration. [#3710](https://github.com/graphprotocol/graph-node/pull/3710) +- The output you'll get from `graphman query` is less cluttered and overall nicer. The new options `--output` and `--trace` are available for detailed query information. [#3860](https://github.com/graphprotocol/graph-node/pull/3860) +- `docker build` will now `--target` the production build stage by default. When you want to get the debug build, you now need `--target graph-node-debug`. [#3814](https://github.com/graphprotocol/graph-node/pull/3814) +- Node IDs can now contain any character. The Docker start script still replaces hyphens with underscores for backwards compatibility reasons, but this behavior can be changed with the `GRAPH_NODE_ID_USE_LITERAL_VALUE` environment variable. With this new option, you can now seamlessly use the K8s-provided host names as node IDs, provided you reassign your deployments accordingly. [#3688](https://github.com/graphprotocol/graph-node/pull/3688) +- You can now use the `conn_pool_size` option in TOML configuration files to configure the connection pool size for Firehose providers. [#3833](https://github.com/graphprotocol/graph-node/pull/3833) +- Index nodes now have an endpoint to perform block number to canonical hash conversion, which will unblock further work towards multichain support. [#3942](https://github.com/graphprotocol/graph-node/pull/3942) +- `_meta.block.timestamp` is now available for subgraphs indexing EVM chains. [#3738](https://github.com/graphprotocol/graph-node/pull/3738), [#3902](https://github.com/graphprotocol/graph-node/pull/3902) +- The `deployment_eth_rpc_request_duration` metric now also observes `eth_getTransactionReceipt` requests' duration. [#3903](https://github.com/graphprotocol/graph-node/pull/3903) +- New Prometheus metrics `query_parsing_time` and `query_validation_time` for monitoring query processing performance. [#3760](https://github.com/graphprotocol/graph-node/pull/3760) +- New command `graphman config provider`, which shows what providers are available for new deployments on a given network and node. [#3816](https://github.com/graphprotocol/graph-node/pull/3816) + E.g. `$ graphman --node-id index_node_0 --config graph-node.toml config provider mainnet` +- Experimental support for GraphQL API versioning has landed. [#3185](https://github.com/graphprotocol/graph-node/pull/3185) +- Progress towards experimental support for off-chain data sources. [#3791](https://github.com/graphprotocol/graph-node/pull/3791) +- Experimental integration for substreams. [#3777](https://github.com/graphprotocol/graph-node/pull/3777), [#3784](https://github.com/graphprotocol/graph-node/pull/3784), [#3897](https://github.com/graphprotocol/graph-node/pull/3897), [#3765](https://github.com/graphprotocol/graph-node/pull/3765), and others -For new subgraph deployments, dynamic data sources will be recorded under the `sgd*.data_sources$` -table, rather than `subgraphs.dynamic_ethereum_contract_data_source`. As a consequence -new deployments will not work correctly on earlier graph node versions, so -_downgrading to an earlier graph node version is not supported_. -See issue #3405 for other details. +### Bug fixes + +- `graphman stats` now complains instead of failing silently when incorrectly setting `account-like` optimizations. [#3918](https://github.com/graphprotocol/graph-node/pull/3918) +- Fixed inconsistent logic in the provider selection when the `limit` TOML configuration option was set. [#3816](https://github.com/graphprotocol/graph-node/pull/3816) +- Fixed issues that would arise from dynamic data sources' names clashing against template names. [#3851](https://github.com/graphprotocol/graph-node/pull/3851) +- Dynamic data sources triggers are now processed by insertion order. [#3851](https://github.com/graphprotocol/graph-node/pull/3851), [#3854](https://github.com/graphprotocol/graph-node/pull/3854) +- When starting, the Docker image now replaces the `bash` process with the `graph-node` process (with a PID of 1). [#3803](https://github.com/graphprotocol/graph-node/pull/3803) +- Refactor subgraph store tests by @evaporei in https://github.com/graphprotocol/graph-node/pull/3662 +- The `ethereum_chain_head_number` metric doesn't get out of sync anymore on chains that use Firehose. [#3771](https://github.com/graphprotocol/graph-node/pull/3771), [#3732](https://github.com/graphprotocol/graph-node/issues/3732) +- Fixed a crash caused by bad block data from the provider. [#3944](https://github.com/graphprotocol/graph-node/pull/3944) +- Fixed some minor Firehose connectivity issues via TCP keepalive, connection and request timeouts, and connection window size tweaks. [#3822](https://github.com/graphprotocol/graph-node/pull/3822), [#3855](https://github.com/graphprotocol/graph-node/pull/3855), [#3877](https://github.com/graphprotocol/graph-node/pull/3877), [#3810](https://github.com/graphprotocol/graph-node/pull/3810), [#3818](https://github.com/graphprotocol/graph-node/pull/3818) +- Copying private data sources' tables across shards now works as expected. [#3836](https://github.com/graphprotocol/graph-node/pull/3836) + +### Performance improvements + +- Firehose GRPC stream requests are now compressed with `gzip`, if the server supports it. [#3893](https://github.com/graphprotocol/graph-node/pull/3893) +- Memory efficiency improvements within the entity cache. [#3594](https://github.com/graphprotocol/graph-node/pull/3594) +- Identical queries now benefit from GraphQL validation caching, and responses are served faster. [#3759](https://github.com/graphprotocol/graph-node/pull/3759) + +### Other + +- Avoid leaking some sensitive information in logs. [#3812](https://github.com/graphprotocol/graph-node/pull/3812) + +### Dependency updates + +| Dependency | PR(s) | Old version | Current version | +|----|----|------|----| +| `serde_yaml` | [#3746](https://github.com/graphprotocol/graph-node/pull/3746) | `v0.8.24` | `v0.8.26` | +| `web3` | [#3806](https://github.com/graphprotocol/graph-node/pull/3806) | `2760dbd` | `7f8eb6d` | +| `clap` | [#3794](https://github.com/graphprotocol/graph-node/pull/3794), [#3848](https://github.com/graphprotocol/graph-node/pull/3848), [#3931](https://github.com/graphprotocol/graph-node/pull/3931) | `v3.2.8` | `3.2.21` | +| `cid` | [#3824](https://github.com/graphprotocol/graph-node/pull/3824) | `v0.8.5` | `v0.8.6` | +| `anyhow` | [#3826](https://github.com/graphprotocol/graph-node/pull/3826), [#3841](https://github.com/graphprotocol/graph-node/pull/3841), [#3865](https://github.com/graphprotocol/graph-node/pull/3865), [#3932](https://github.com/graphprotocol/graph-node/pull/3932) | `v1.0.57` | `1.0.65` | +| `chrono` | [#3827](https://github.com/graphprotocol/graph-node/pull/3827), [#3849](https://github.com/graphprotocol/graph-node/pull/3839), [#3868](https://github.com/graphprotocol/graph-node/pull/3868) | `v0.4.19` | `v0.4.22` | +| `proc-macro2` | [#3845](https://github.com/graphprotocol/graph-node/pull/3845) | `v1.0.40` | `1.0.43` | +| `ethabi` | [#3847](https://github.com/graphprotocol/graph-node/pull/3847) | `v17.1.0` | `v17.2.0` | +| `once_cell` | [#3870](https://github.com/graphprotocol/graph-node/pull/3870) | `v1.13.0` | `v1.13.1` | +| `either` | [#3869](https://github.com/graphprotocol/graph-node/pull/3869) | `v1.7.0` | `v1.8.0` | +| `sha2` | [#3904](https://github.com/graphprotocol/graph-node/pull/3904) | `v0.10.2` | `v0.10.5` | +| `mockall` | [#3776](https://github.com/graphprotocol/graph-node/pull/3776) | `v0.9.1` | removed | +| `croosbeam` | [#3772](https://github.com/graphprotocol/graph-node/pull/3772) | `v0.8.1` | `v0.8.2` | +| `async-recursion` | [#3873](https://github.com/graphprotocol/graph-node/pull/3873) | none | `v1.0.0` | + + ## 0.27.0 diff --git a/chain/arweave/Cargo.toml b/chain/arweave/Cargo.toml index 5d94dd946dc..6538c7ce2a1 100644 --- a/chain/arweave/Cargo.toml +++ b/chain/arweave/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-arweave" -version = "0.27.0" +version = "0.28.0" edition = "2021" [build-dependencies] diff --git a/chain/common/Cargo.toml b/chain/common/Cargo.toml index 364e8e33a12..5c6d2517141 100644 --- a/chain/common/Cargo.toml +++ b/chain/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-common" -version = "0.27.0" +version = "0.28.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/chain/cosmos/Cargo.toml b/chain/cosmos/Cargo.toml index de085f5e27a..b08c307b783 100644 --- a/chain/cosmos/Cargo.toml +++ b/chain/cosmos/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-cosmos" -version = "0.27.0" +version = "0.28.0" edition = "2018" [build-dependencies] diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index b3143cd1f89..f322e1cb227 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-ethereum" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/chain/near/Cargo.toml b/chain/near/Cargo.toml index e61b5609477..fef1dc2b50f 100644 --- a/chain/near/Cargo.toml +++ b/chain/near/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-near" -version = "0.27.0" +version = "0.28.0" edition = "2021" [build-dependencies] diff --git a/chain/substreams/Cargo.toml b/chain/substreams/Cargo.toml index bc9bdbca51a..e1b494e2d76 100644 --- a/chain/substreams/Cargo.toml +++ b/chain/substreams/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-chain-substreams" -version = "0.27.0" +version = "0.28.0" edition = "2021" [build-dependencies] diff --git a/core/Cargo.toml b/core/Cargo.toml index 94f16819c79..db121acb844 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-core" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 6062689cc86..2e9be3efce1 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/graphql/Cargo.toml b/graphql/Cargo.toml index a973e20701e..d2c9f86b769 100644 --- a/graphql/Cargo.toml +++ b/graphql/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-graphql" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/mock/Cargo.toml b/mock/Cargo.toml index 7a3a79aadc6..54efd5e1bc9 100644 --- a/mock/Cargo.toml +++ b/mock/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-mock" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/node/Cargo.toml b/node/Cargo.toml index ddee868515b..140ac537951 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-node" -version = "0.27.0" +version = "0.28.0" edition = "2021" default-run = "graph-node" diff --git a/runtime/derive/Cargo.toml b/runtime/derive/Cargo.toml index 13013d7e01a..2ae12aef900 100644 --- a/runtime/derive/Cargo.toml +++ b/runtime/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-runtime-derive" -version = "0.27.0" +version = "0.28.0" edition = "2021" [lib] diff --git a/runtime/test/Cargo.toml b/runtime/test/Cargo.toml index cd85bbc4226..c9b8a3c5644 100644 --- a/runtime/test/Cargo.toml +++ b/runtime/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-runtime-test" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index 32818eb8697..8c5cc02481a 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-runtime-wasm" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/server/http/Cargo.toml b/server/http/Cargo.toml index 34387f4c3a6..f39067d4bd1 100644 --- a/server/http/Cargo.toml +++ b/server/http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-http" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/server/index-node/Cargo.toml b/server/index-node/Cargo.toml index 15943699963..49fcf2b896d 100644 --- a/server/index-node/Cargo.toml +++ b/server/index-node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-index-node" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/server/json-rpc/Cargo.toml b/server/json-rpc/Cargo.toml index 7e7e4d9afa0..55123d81199 100644 --- a/server/json-rpc/Cargo.toml +++ b/server/json-rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-json-rpc" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/server/metrics/Cargo.toml b/server/metrics/Cargo.toml index 8f47abf3d72..0a8b7c196e2 100644 --- a/server/metrics/Cargo.toml +++ b/server/metrics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-metrics" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/server/websocket/Cargo.toml b/server/websocket/Cargo.toml index cf1f8e8d995..bb2efe36bf0 100644 --- a/server/websocket/Cargo.toml +++ b/server/websocket/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-server-websocket" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index f93a2d047e6..4c981b8776e 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-store-postgres" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] diff --git a/store/test-store/Cargo.toml b/store/test-store/Cargo.toml index de9dbe691cb..63793171daa 100644 --- a/store/test-store/Cargo.toml +++ b/store/test-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-store" -version = "0.27.0" +version = "0.28.0" authors = ["Leonardo Yvens "] edition = "2021" description = "Provides static store instance for tests." diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 8c7bd04de6a..62a7f4eeb9f 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graph-tests" -version = "0.27.0" +version = "0.28.0" edition = "2021" [dependencies] From 4ee3eecb60616e0e1c3b8b95458c62c490dd5a26 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Mon, 26 Sep 2022 17:31:06 +0100 Subject: [PATCH 034/253] fix(runtime): Add context to errors (#3991) --- runtime/wasm/src/mapping.rs | 3 ++- runtime/wasm/src/module/mod.rs | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/runtime/wasm/src/mapping.rs b/runtime/wasm/src/mapping.rs index cd1198bbbdf..f625de41957 100644 --- a/runtime/wasm/src/mapping.rs +++ b/runtime/wasm/src/mapping.rs @@ -99,7 +99,8 @@ where host_metrics.cheap_clone(), timeout, experimental_features, - )?; + ) + .context("module instantiation failed")?; section.end(); let _section = host_metrics.stopwatch.start_section("run_handler"); diff --git a/runtime/wasm/src/module/mod.rs b/runtime/wasm/src/module/mod.rs index 327b00e5e85..8e09e45a2ff 100644 --- a/runtime/wasm/src/module/mod.rs +++ b/runtime/wasm/src/module/mod.rs @@ -207,11 +207,15 @@ impl WasmInstance { .get_func(handler) .with_context(|| format!("function {} not found", handler))?; + let func = func + .typed() + .context("wasm function has incorrect signature")?; + // Caution: Make sure all exit paths from this function call `exit_handler`. self.instance_ctx_mut().ctx.state.enter_handler(); // This `match` will return early if there was a non-deterministic trap. - let deterministic_error: Option = match func.typed()?.call(arg.wasm_ptr()) { + let deterministic_error: Option = match func.call(arg.wasm_ptr()) { Ok(()) => None, Err(trap) if self.instance_ctx().possible_reorg => { self.instance_ctx_mut().ctx.state.exit_handler(); From c6e59af6a61ab97e4f6970e2a61fbfcfba32c08c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 09:47:07 +0100 Subject: [PATCH 035/253] build(deps): bump sha2 from 0.10.5 to 0.10.6 (#3954) Bumps [sha2](https://github.com/RustCrypto/hashes) from 0.10.5 to 0.10.6. - [Release notes](https://github.com/RustCrypto/hashes/releases) - [Commits](https://github.com/RustCrypto/hashes/compare/sha2-v0.10.5...sha2-v0.10.6) --- updated-dependencies: - dependency-name: sha2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 20 ++++++++++---------- chain/arweave/Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d2db6054c9..6214d12a10d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -331,7 +331,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "constant_time_eq", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -1019,9 +1019,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer 0.10.2", "crypto-common", @@ -1548,7 +1548,7 @@ dependencies = [ "prost", "prost-types", "serde", - "sha2 0.10.5", + "sha2 0.10.6", "tonic-build", ] @@ -2654,9 +2654,9 @@ dependencies = [ "blake2s_simd", "blake3 1.3.1", "core2", - "digest 0.10.3", + "digest 0.10.5", "multihash-derive", - "sha2 0.10.5", + "sha2 0.10.6", "sha3", "unsigned-varint", ] @@ -3854,13 +3854,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures 0.2.2", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -3869,7 +3869,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", "keccak", ] diff --git a/chain/arweave/Cargo.toml b/chain/arweave/Cargo.toml index 6538c7ce2a1..472ee85ff32 100644 --- a/chain/arweave/Cargo.toml +++ b/chain/arweave/Cargo.toml @@ -12,7 +12,7 @@ graph = { path = "../../graph" } prost = "0.10.1" prost-types = "0.10.1" serde = "1.0" -sha2 = "0.10.5" +sha2 = "0.10.6" graph-runtime-wasm = { path = "../../runtime/wasm" } graph-runtime-derive = { path = "../../runtime/derive" } From e7a1c4ce04c596771ecd0c1c5747a8d8bbb808e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 09:47:48 +0100 Subject: [PATCH 036/253] build(deps): bump env_logger from 0.9.0 to 0.9.1 (#3955) Bumps [env_logger](https://github.com/env-logger-rs/env_logger) from 0.9.0 to 0.9.1. - [Release notes](https://github.com/env-logger-rs/env_logger/releases) - [Changelog](https://github.com/env-logger-rs/env_logger/blob/main/CHANGELOG.md) - [Commits](https://github.com/env-logger-rs/env_logger/compare/v0.9.0...v0.9.1) --- updated-dependencies: - dependency-name: env_logger dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 6 +++--- node/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6214d12a10d..5513302ebf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1095,9 +1095,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", "humantime 2.1.0", @@ -1715,7 +1715,7 @@ dependencies = [ "clap", "crossbeam-channel", "diesel", - "env_logger 0.9.0", + "env_logger 0.9.1", "futures 0.3.16", "git-testament", "graph", diff --git a/node/Cargo.toml b/node/Cargo.toml index 140ac537951..4ff3a05358a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -14,7 +14,7 @@ path = "src/bin/manager.rs" [dependencies] clap = { version = "3.2.22", features = ["derive", "env"] } -env_logger = "0.9.0" +env_logger = "0.9.1" git-testament = "0.2" graphql-parser = "0.4.0" futures = { version = "0.3.1", features = ["compat"] } From 0bb4668ef6c3b5d6c265362fb88a5ee8c5f430bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 09:48:18 +0100 Subject: [PATCH 037/253] build(deps): bump url from 2.2.2 to 2.3.0 (#3956) Bumps [url](https://github.com/servo/rust-url) from 2.2.2 to 2.3.0. - [Release notes](https://github.com/servo/rust-url/releases) - [Commits](https://github.com/servo/rust-url/compare/v2.2.2...v2.3.0) --- updated-dependencies: - dependency-name: url dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 5 ++--- graph/Cargo.toml | 2 +- node/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5513302ebf0..f3967dd4999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4757,13 +4757,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 2e9be3efce1..372c308008a 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -46,7 +46,7 @@ tiny-keccak = "1.5.0" tokio = { version = "1.16.1", features = ["time", "sync", "macros", "test-util", "rt-multi-thread", "parking_lot"] } tokio-stream = { version = "0.1.9", features = ["sync"] } tokio-retry = "0.3.0" -url = "2.2.1" +url = "2.3.0" prometheus = "0.13.2" priority-queue = "0.7.0" tonic = { version = "0.7.1", features = ["tls-roots","compression"] } diff --git a/node/Cargo.toml b/node/Cargo.toml index 4ff3a05358a..f711bf601e2 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -19,7 +19,7 @@ git-testament = "0.2" graphql-parser = "0.4.0" futures = { version = "0.3.1", features = ["compat"] } lazy_static = "1.2.0" -url = "2.2.1" +url = "2.3.0" crossbeam-channel = "0.5.5" graph = { path = "../graph" } graph-core = { path = "../core" } From 750730ce1a0d02c76a7660aef4cf29702bcbbd77 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Tue, 27 Sep 2022 19:05:49 +0100 Subject: [PATCH 038/253] firehose: Dont use debug format on firehose errors (#3990) --- graph/src/blockchain/firehose_block_ingestor.rs | 6 +++--- graph/src/blockchain/substreams_block_stream.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/graph/src/blockchain/firehose_block_ingestor.rs b/graph/src/blockchain/firehose_block_ingestor.rs index 965272c0d35..54691bc5026 100644 --- a/graph/src/blockchain/firehose_block_ingestor.rs +++ b/graph/src/blockchain/firehose_block_ingestor.rs @@ -73,7 +73,7 @@ where latest_cursor = self.process_blocks(latest_cursor, stream).await } Err(e) => { - error!(self.logger, "Unable to connect to endpoint: {:?}", e); + error!(self.logger, "Unable to connect to endpoint: {:#}", e); } } @@ -89,7 +89,7 @@ where match self.chain_store.clone().chain_head_cursor() { Ok(cursor) => return cursor.unwrap_or_else(|| "".to_string()), Err(e) => { - error!(self.logger, "Fetching chain head cursor failed: {:?}", e); + error!(self.logger, "Fetching chain head cursor failed: {:#}", e); backoff.sleep_async().await; } @@ -128,7 +128,7 @@ where }; if let Err(e) = result { - error!(self.logger, "Process block failed: {:?}", e); + error!(self.logger, "Process block failed: {:#}", e); break; } diff --git a/graph/src/blockchain/substreams_block_stream.rs b/graph/src/blockchain/substreams_block_stream.rs index 291e14b210f..6581df743c9 100644 --- a/graph/src/blockchain/substreams_block_stream.rs +++ b/graph/src/blockchain/substreams_block_stream.rs @@ -264,7 +264,7 @@ fn stream_blocks>( metrics.observe_failed_connection(&mut connect_start); - error!(logger, "Unable to connect to endpoint: {:?}", e); + error!(logger, "Unable to connect to endpoint: {:#}", e); } } @@ -287,7 +287,7 @@ async fn process_substreams_response>( ) -> Result>, Error> { let response = match result { Ok(v) => v, - Err(e) => return Err(anyhow!("An error occurred while streaming blocks: {:?}", e)), + Err(e) => return Err(anyhow!("An error occurred while streaming blocks: {:#}", e)), }; match response.message { From e26866b78bbb0e0c748f644a80d89d04504be989 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 19:35:04 +0100 Subject: [PATCH 039/253] build(deps): bump tokio-stream from 0.1.9 to 0.1.10 (#3957) Bumps [tokio-stream](https://github.com/tokio-rs/tokio) from 0.1.9 to 0.1.10. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-stream-0.1.9...tokio-stream-0.1.10) --- updated-dependencies: - dependency-name: tokio-stream dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- graph/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3967dd4999..619f5fa378e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4384,9 +4384,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" dependencies = [ "futures-core", "pin-project-lite", diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 372c308008a..e4604daa0af 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -44,7 +44,7 @@ slog-term = "2.7.0" petgraph = "0.6.2" tiny-keccak = "1.5.0" tokio = { version = "1.16.1", features = ["time", "sync", "macros", "test-util", "rt-multi-thread", "parking_lot"] } -tokio-stream = { version = "0.1.9", features = ["sync"] } +tokio-stream = { version = "0.1.10", features = ["sync"] } tokio-retry = "0.3.0" url = "2.3.0" prometheus = "0.13.2" From 29042a19f245d205f7e24f559833fa8d73686fad Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Wed, 28 Sep 2022 14:51:51 +0100 Subject: [PATCH 040/253] fix(offchain): Always wake request queue on push (#4003) * fix(offchain): Always wake request queue on push * fix(offchain): Improve polling monitor cancellation detection --- core/src/polling_monitor/mod.rs | 156 +++++++++++++++++--------------- 1 file changed, 83 insertions(+), 73 deletions(-) diff --git a/core/src/polling_monitor/mod.rs b/core/src/polling_monitor/mod.rs index cb8dbf4c340..008d2f9bdad 100644 --- a/core/src/polling_monitor/mod.rs +++ b/core/src/polling_monitor/mod.rs @@ -4,11 +4,12 @@ mod metrics; use std::fmt::Display; use std::sync::Arc; -use futures::stream; use futures::stream::StreamExt; +use futures::{stream, FutureExt}; use graph::cheap_clone::CheapClone; use graph::parking_lot::Mutex; use graph::prelude::tokio; +use graph::prometheus::{Counter, Gauge}; use graph::slog::{debug, Logger}; use graph::util::monitored::MonitoredVecDeque as VecDeque; use tokio::sync::{mpsc, watch}; @@ -16,6 +17,35 @@ use tower::{Service, ServiceExt}; pub use self::metrics::PollingMonitorMetrics; +// A queue that notifies `waker` whenever an element is pushed. +struct Queue { + queue: Mutex>, + waker: watch::Sender<()>, +} + +impl Queue { + fn new(depth: Gauge, popped: Counter) -> (Arc, watch::Receiver<()>) { + let queue = Mutex::new(VecDeque::new(depth, popped)); + let (waker, woken) = watch::channel(()); + let this = Queue { queue, waker }; + (Arc::new(this), woken) + } + + fn push_back(&self, e: T) { + self.queue.lock().push_back(e); + let _ = self.waker.send(()); + } + + fn push_front(&self, e: T) { + self.queue.lock().push_front(e); + let _ = self.waker.send(()); + } + + fn pop_front(&self) -> Option { + self.queue.lock().pop_front() + } +} + /// Spawn a monitor that actively polls a service. Whenever the service has capacity, the monitor /// pulls object ids from the queue and polls the service. If the object is not present or in case /// of error, the object id is pushed to the back of the queue to be polled again. @@ -34,30 +64,35 @@ where E: Display + Send + 'static, S::Future: Send, { - let queue = Arc::new(Mutex::new(VecDeque::new( - metrics.queue_depth.clone(), - metrics.requests.clone(), - ))); - let (wake_up_queue, queue_woken) = watch::channel(()); + let (queue, queue_woken) = Queue::new(metrics.queue_depth.clone(), metrics.requests.clone()); + let cancel_check = response_sender.clone(); let queue_to_stream = { let queue = queue.cheap_clone(); stream::unfold((), move |()| { let queue = queue.cheap_clone(); let mut queue_woken = queue_woken.clone(); + let cancel_check = cancel_check.clone(); async move { loop { - let id = queue.lock().pop_front(); + if cancel_check.is_closed() { + break None; + } + + let id = queue.pop_front(); match id { Some(id) => break Some((id, ())), - None => match queue_woken.changed().await { - // Queue woken, check it. - Ok(()) => {} - - // The `PollingMonitor` has been dropped, cancel this task. - Err(_) => break None, - }, - }; + + // Nothing on the queue, wait for a queue wake up or cancellation. + None => { + futures::future::select( + // Unwrap: `queue` holds a sender. + queue_woken.changed().map(|r| r.unwrap()).boxed(), + cancel_check.closed().boxed(), + ) + .await; + } + } } } }) @@ -80,7 +115,7 @@ where // Object not found, push the id to the back of the queue. Ok((id, None)) => { metrics.not_found.inc(); - queue.lock().push_back(id); + queue.push_back(id); } // Error polling, log it and push the id to the back of the queue. @@ -89,38 +124,26 @@ where "error" => format!("{:#}", e), "object_id" => id.to_string()); metrics.errors.inc(); - queue.lock().push_back(id); + queue.push_back(id); } } } }); } - PollingMonitor { - queue, - wake_up_queue, - } + PollingMonitor { queue } } /// Handle for adding objects to be monitored. pub struct PollingMonitor { - queue: Arc>>, - - // This serves two purposes, to wake up the monitor when an item arrives on an empty queue, and - // to stop the montior task when this handle is dropped. - wake_up_queue: watch::Sender<()>, + queue: Arc>, } impl PollingMonitor { /// Add an object id to the polling queue. New requests have priority and are pushed to the /// front of the queue. pub fn monitor(&self, id: ID) { - let mut queue = self.queue.lock(); - if queue.is_empty() { - // If the send fails, the response receiver has been dropped, so this handle is useless. - let _ = self.wake_up_queue.send(()); - } - queue.push_front(id); + self.queue.push_front(id); } } @@ -160,16 +183,25 @@ mod tests { handle.next_request().await.unwrap().1.send_response(res) } - #[tokio::test] - async fn polling_monitor_simple() { - let (svc, mut handle) = mock::pair(); - let (tx, mut rx) = mpsc::channel(10); + fn setup() -> ( + mock::Handle<&'static str, Option<&'static str>>, + PollingMonitor<&'static str>, + mpsc::Receiver<(&'static str, &'static str)>, + ) { + let (svc, handle) = mock::pair(); + let (tx, rx) = mpsc::channel(10); let monitor = spawn_monitor( MockService(svc), tx, log::discard(), PollingMonitorMetrics::mock(), ); + (handle, monitor, rx) + } + + #[tokio::test] + async fn polling_monitor_simple() { + let (mut handle, monitor, mut rx) = setup(); // Basic test, single file is immediately available. monitor.monitor("req-0"); @@ -179,14 +211,7 @@ mod tests { #[tokio::test] async fn polling_monitor_unordered() { - let (svc, mut handle) = mock::pair(); - let (tx, mut rx) = mpsc::channel(10); - let monitor = spawn_monitor( - MockService(svc), - tx, - log::discard(), - PollingMonitorMetrics::mock(), - ); + let (mut handle, monitor, mut rx) = setup(); // Test unorderedness of the response stream, and the LIFO semantics of `monitor`. // @@ -203,12 +228,7 @@ mod tests { #[tokio::test] async fn polling_monitor_failed_push_to_back() { - let (svc, mut handle) = mock::pair(); - let (tx, mut rx) = mpsc::channel(10); - - // Limit service to one request at a time. - let svc = tower::limit::ConcurrencyLimit::new(MockService(svc), 1); - let monitor = spawn_monitor(svc, tx, log::discard(), PollingMonitorMetrics::mock()); + let (mut handle, monitor, mut rx) = setup(); // Test that objects not found go on the back of the queue. monitor.monitor("req-0"); @@ -232,32 +252,22 @@ mod tests { #[tokio::test] async fn polling_monitor_cancelation() { - let (svc, _handle) = mock::pair(); - let (tx, mut rx) = mpsc::channel(10); - let monitor = spawn_monitor( - MockService(svc), - tx, - log::discard(), - PollingMonitorMetrics::mock(), - ); - - // Cancelation on monitor drop. - drop(monitor); - assert_eq!(rx.recv().await, None); - - let (svc, mut handle) = mock::pair(); - let (tx, rx) = mpsc::channel(10); - let monitor = spawn_monitor( - MockService(svc), - tx, - log::discard(), - PollingMonitorMetrics::mock(), - ); + // Cancelation on receiver drop, no pending request. + let (mut handle, _monitor, rx) = setup(); + drop(rx); + assert!(handle.next_request().await.is_none()); - // Cancelation on receiver drop. + // Cancelation on receiver drop, with pending request. + let (mut handle, monitor, rx) = setup(); monitor.monitor("req-0"); drop(rx); - send_response(&mut handle, Some("res-0")).await; assert!(handle.next_request().await.is_none()); + + // Cancelation on receiver drop, while queue is waiting. + let (mut handle, _monitor, rx) = setup(); + let handle = tokio::spawn(async move { handle.next_request().await }); + tokio::task::yield_now().await; + drop(rx); + assert!(handle.await.unwrap().is_none()); } } From 487c35efff7a5f849ca54c155ee0ea6a7e698d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Pie=C5=84kowski?= Date: Thu, 29 Sep 2022 16:01:13 +0200 Subject: [PATCH 041/253] Cosmos: Add the message handler (#3975) * Add Message Handler * Fix struct and field name error in padding unit test * Fix the compilation errors * Use a unique field number * Pass transaction context to handlers * Remove unused methods Co-authored-by: Seb-figment Co-authored-by: Sebastien Nobert <98420516+Seb-figment@users.noreply.github.com> Co-authored-by: Robert Ignat Co-authored-by: roignpar <47150492+roignpar@users.noreply.github.com> --- chain/common/src/lib.rs | 2 +- .../resources/firehose/annotations.proto | 8 +- chain/cosmos/proto/firehose/annotations.proto | 8 +- chain/cosmos/proto/type.proto | 15 ++ chain/cosmos/src/chain.rs | 92 ++++++++--- chain/cosmos/src/codec.rs | 39 ++--- chain/cosmos/src/data_source.rs | 113 +++++++++++++ .../src/protobuf/sf.cosmos.r#type.v1.rs | 30 ++++ chain/cosmos/src/runtime/mod.rs | 15 ++ chain/cosmos/src/trigger.rs | 153 ++++++++++++++++-- graph/src/runtime/mod.rs | 7 +- 11 files changed, 417 insertions(+), 65 deletions(-) diff --git a/chain/common/src/lib.rs b/chain/common/src/lib.rs index f1e5aeb504c..694a6131a7e 100644 --- a/chain/common/src/lib.rs +++ b/chain/common/src/lib.rs @@ -12,7 +12,7 @@ use protobuf::UnknownValueRef; use std::convert::From; use std::path::Path; -const REQUIRED_ID: u32 = 66001; +const REQUIRED_ID: u32 = 77001; #[derive(Debug, Clone)] pub struct Field { diff --git a/chain/common/tests/resources/firehose/annotations.proto b/chain/common/tests/resources/firehose/annotations.proto index 2541e7f3576..1476c1ab08d 100644 --- a/chain/common/tests/resources/firehose/annotations.proto +++ b/chain/common/tests/resources/firehose/annotations.proto @@ -1,9 +1,11 @@ +syntax = "proto3"; + package firehose; -import "google/protobuf/descriptor.proto"; +option go_package = "github.com/streamingfast/pbgo/sf/firehose/v1;pbfirehose"; -// 66K range is arbitrary picked number, might be conflict +import "google/protobuf/descriptor.proto"; extend google.protobuf.FieldOptions { - optional bool required = 66001; + optional bool required = 77001; } diff --git a/chain/cosmos/proto/firehose/annotations.proto b/chain/cosmos/proto/firehose/annotations.proto index 2541e7f3576..1476c1ab08d 100644 --- a/chain/cosmos/proto/firehose/annotations.proto +++ b/chain/cosmos/proto/firehose/annotations.proto @@ -1,9 +1,11 @@ +syntax = "proto3"; + package firehose; -import "google/protobuf/descriptor.proto"; +option go_package = "github.com/streamingfast/pbgo/sf/firehose/v1;pbfirehose"; -// 66K range is arbitrary picked number, might be conflict +import "google/protobuf/descriptor.proto"; extend google.protobuf.FieldOptions { - optional bool required = 66001; + optional bool required = 77001; } diff --git a/chain/cosmos/proto/type.proto b/chain/cosmos/proto/type.proto index 19db384e1dc..c32502da1e9 100644 --- a/chain/cosmos/proto/type.proto +++ b/chain/cosmos/proto/type.proto @@ -33,6 +33,7 @@ message HeaderOnlyBlock { message EventData { Event event = 1 [(firehose.required) = true]; HeaderOnlyBlock block = 2 [(firehose.required) = true]; + TransactionContext tx = 3; } message TransactionData { @@ -40,6 +41,20 @@ message TransactionData { HeaderOnlyBlock block = 2 [(firehose.required) = true]; } +message MessageData { + google.protobuf.Any message = 1 [(firehose.required) = true]; + HeaderOnlyBlock block = 2 [(firehose.required) = true]; + TransactionContext tx = 3 [(firehose.required) = true]; +} + +message TransactionContext { + bytes hash = 1; + uint32 index = 2; + uint32 code = 3; + int64 gas_wanted = 4; + int64 gas_used = 5; +} + message Header { Consensus version = 1 [(gogoproto.nullable) = false]; string chain_id = 2 [(gogoproto.customname) = "ChainID"]; diff --git a/chain/cosmos/src/chain.rs b/chain/cosmos/src/chain.rs index 9e6254662ed..fdec80ce9fb 100644 --- a/chain/cosmos/src/chain.rs +++ b/chain/cosmos/src/chain.rs @@ -178,6 +178,14 @@ pub struct TriggersAdapter {} #[async_trait] impl TriggersAdapterTrait for TriggersAdapter { + async fn ancestor_block( + &self, + _ptr: BlockPtr, + _offset: BlockNumber, + ) -> Result, Error> { + panic!("Should never be called since not used by FirehoseBlockStream") + } + async fn scan_triggers( &self, _from: BlockNumber, @@ -204,10 +212,30 @@ impl TriggersAdapterTrait for TriggersAdapter { // block. This is not currently possible because EventData is automatically // generated. .filter_map(|event| { - filter_event_trigger(filter, event, &header_only_block, EventOrigin::BeginBlock) + filter_event_trigger( + filter, + event, + &header_only_block, + None, + EventOrigin::BeginBlock, + ) }) - .chain(shared_block.tx_events()?.cloned().filter_map(|event| { - filter_event_trigger(filter, event, &header_only_block, EventOrigin::DeliverTx) + .chain(shared_block.transactions().flat_map(|tx| { + tx.result + .as_ref() + .unwrap() + .events + .iter() + .filter_map(|e| { + filter_event_trigger( + filter, + e.clone(), + &header_only_block, + Some(build_tx_context(tx)), + EventOrigin::DeliverTx, + ) + }) + .collect::>() })) .chain( shared_block @@ -218,18 +246,32 @@ impl TriggersAdapterTrait for TriggersAdapter { filter, event, &header_only_block, + None, EventOrigin::EndBlock, ) }), ) .collect(); - triggers.extend( - shared_block - .transactions() - .cloned() - .map(|tx| CosmosTrigger::with_transaction(tx, header_only_block.clone())), - ); + triggers.extend(shared_block.transactions().cloned().flat_map(|tx_result| { + let mut triggers: Vec<_> = Vec::new(); + if let Some(tx) = tx_result.tx.clone() { + if let Some(tx_body) = tx.body { + triggers.extend(tx_body.messages.into_iter().map(|message| { + CosmosTrigger::with_message( + message, + header_only_block.clone(), + build_tx_context(&tx_result), + ) + })); + } + } + triggers.push(CosmosTrigger::with_transaction( + tx_result, + header_only_block.clone(), + )); + triggers + })); if filter.block_filter.trigger_every_block { triggers.push(CosmosTrigger::Block(shared_block.cheap_clone())); @@ -242,14 +284,6 @@ impl TriggersAdapterTrait for TriggersAdapter { panic!("Should never be called since not used by FirehoseBlockStream") } - async fn ancestor_block( - &self, - _ptr: BlockPtr, - _offset: BlockNumber, - ) -> Result, Error> { - panic!("Should never be called since not used by FirehoseBlockStream") - } - /// Panics if `block` is genesis. /// But that's ok since this is only called when reverting `block`. async fn parent_ptr(&self, block: &BlockPtr) -> Result, Error> { @@ -265,15 +299,31 @@ fn filter_event_trigger( filter: &TriggerFilter, event: codec::Event, block: &codec::HeaderOnlyBlock, + tx_context: Option, origin: EventOrigin, ) -> Option { if filter.event_type_filter.matches(&event.event_type) { - Some(CosmosTrigger::with_event(event, block.clone(), origin)) + Some(CosmosTrigger::with_event( + event, + block.clone(), + tx_context, + origin, + )) } else { None } } +fn build_tx_context(tx: &codec::TxResult) -> codec::TransactionContext { + codec::TransactionContext { + hash: tx.hash.clone(), + index: tx.index, + code: tx.result.as_ref().unwrap().code, + gas_wanted: tx.result.as_ref().unwrap().gas_wanted, + gas_used: tx.result.as_ref().unwrap().gas_used, + } +} + pub struct FirehoseMapper { endpoint: Arc, } @@ -408,16 +458,19 @@ mod test { CosmosTrigger::with_event( Event::test_with_type("begin_event_3"), header_only_block.clone(), + None, EventOrigin::BeginBlock, ), CosmosTrigger::with_event( Event::test_with_type("tx_event_3"), header_only_block.clone(), + Some(build_tx_context(&block_with_events.transactions[2])), EventOrigin::DeliverTx, ), CosmosTrigger::with_event( Event::test_with_type("end_event_3"), header_only_block.clone(), + None, EventOrigin::EndBlock, ), CosmosTrigger::with_transaction( @@ -442,16 +495,19 @@ mod test { CosmosTrigger::with_event( Event::test_with_type("begin_event_3"), header_only_block.clone(), + None, EventOrigin::BeginBlock, ), CosmosTrigger::with_event( Event::test_with_type("tx_event_2"), header_only_block.clone(), + Some(build_tx_context(&block_with_events.transactions[1])), EventOrigin::DeliverTx, ), CosmosTrigger::with_event( Event::test_with_type("end_event_1"), header_only_block.clone(), + None, EventOrigin::EndBlock, ), CosmosTrigger::with_transaction( diff --git a/chain/cosmos/src/codec.rs b/chain/cosmos/src/codec.rs index 7b2a2997f90..fae145a449e 100644 --- a/chain/cosmos/src/codec.rs +++ b/chain/cosmos/src/codec.rs @@ -15,15 +15,6 @@ impl Block { .ok_or_else(|| anyhow!("block data missing header field")) } - pub fn events(&self) -> Result, Error> { - let events = self - .begin_block_events()? - .chain(self.tx_events()?) - .chain(self.end_block_events()?); - - Ok(events) - } - pub fn begin_block_events(&self) -> Result, Error> { let events = self .result_begin_block @@ -35,22 +26,6 @@ impl Block { Ok(events) } - pub fn tx_events(&self) -> Result, Error> { - if self.transactions.iter().any(|tx| tx.result.is_none()) { - return Err(anyhow!("block data transaction missing result field")); - } - - let events = self.transactions.iter().flat_map(|tx| { - tx.result - .as_ref() - .map(|b| b.events.iter()) - .into_iter() - .flatten() - }); - - Ok(events) - } - pub fn end_block_events(&self) -> Result, Error> { let events = self .result_end_block @@ -197,3 +172,17 @@ impl TransactionData { .ok_or_else(|| anyhow!("transaction data missing block field")) } } + +impl MessageData { + pub fn message(&self) -> Result<&prost_types::Any, Error> { + self.message + .as_ref() + .ok_or_else(|| anyhow!("message data missing message field")) + } + + pub fn block(&self) -> Result<&HeaderOnlyBlock, Error> { + self.block + .as_ref() + .ok_or_else(|| anyhow!("message data missing block field")) + } +} diff --git a/chain/cosmos/src/data_source.rs b/chain/cosmos/src/data_source.rs index 7535a50668e..376628ba96d 100644 --- a/chain/cosmos/src/data_source.rs +++ b/chain/cosmos/src/data_source.rs @@ -72,6 +72,13 @@ impl blockchain::DataSource for DataSource { Some(handler) => handler.handler, None => return Ok(None), }, + + CosmosTrigger::Message(message_data) => { + match self.handler_for_message(message_data.message()?) { + Some(handler) => handler.handler, + None => return Ok(None), + } + } }; Ok(Some(TriggerWithHandler::::new( @@ -125,6 +132,7 @@ impl blockchain::DataSource for DataSource { && mapping.block_handlers == other.mapping.block_handlers && mapping.event_handlers == other.mapping.event_handlers && mapping.transaction_handlers == other.mapping.transaction_handlers + && mapping.message_handlers == other.mapping.message_handlers && context == &other.context } @@ -191,6 +199,14 @@ impl blockchain::DataSource for DataSource { } } + // Ensure each message handlers is unique + let mut message_type_urls = HashSet::with_capacity(self.mapping.message_handlers.len()); + for message_handler in self.mapping.message_handlers.iter() { + if !message_type_urls.insert(message_handler.message.clone()) { + errors.push(duplicate_url_type(&message_handler.message)) + } + } + errors } @@ -234,6 +250,14 @@ impl DataSource { self.mapping.transaction_handlers.first().cloned() } + fn handler_for_message(&self, message: &::prost_types::Any) -> Option { + self.mapping + .message_handlers + .iter() + .find(|handler| handler.message == message.type_url) + .cloned() + } + fn handler_for_event( &self, event: &codec::Event, @@ -363,6 +387,8 @@ pub struct UnresolvedMapping { pub event_handlers: Vec, #[serde(default)] pub transaction_handlers: Vec, + #[serde(default)] + pub message_handlers: Vec, pub file: Link, } @@ -379,6 +405,7 @@ impl UnresolvedMapping { block_handlers, event_handlers, transaction_handlers, + message_handlers, file: link, } = self; @@ -394,6 +421,7 @@ impl UnresolvedMapping { block_handlers: block_handlers.clone(), event_handlers: event_handlers.clone(), transaction_handlers: transaction_handlers.clone(), + message_handlers: message_handlers.clone(), runtime: Arc::new(module_bytes), link, }) @@ -408,6 +436,7 @@ pub struct Mapping { pub block_handlers: Vec, pub event_handlers: Vec, pub transaction_handlers: Vec, + pub message_handlers: Vec, pub runtime: Arc>, pub link: Link, } @@ -429,6 +458,12 @@ pub struct MappingTransactionHandler { pub handler: String, } +#[derive(Clone, Debug, Hash, Eq, PartialEq, Deserialize)] +pub struct MappingMessageHandler { + pub message: String, + pub handler: String, +} + #[derive(Clone, Debug, Hash, Eq, PartialEq, Deserialize)] pub struct Source { #[serde(rename = "startBlock", default)] @@ -462,6 +497,13 @@ fn combined_origins_err(event_type: &str) -> Error { ) } +fn duplicate_url_type(message: &str) -> Error { + anyhow!( + "data source has more than one message handler for message {} ", + message + ) +} + #[cfg(test)] mod tests { use super::*; @@ -524,6 +566,54 @@ mod tests { } } + #[test] + fn test_message_handlers_duplicate() { + let cases = [ + ( + DataSource::with_message_handlers(vec![ + MappingMessageHandler { + handler: "handler".to_string(), + message: "message_0".to_string(), + }, + MappingMessageHandler { + handler: "handler".to_string(), + message: "message_1".to_string(), + }, + ]), + vec![], + ), + ( + DataSource::with_message_handlers(vec![ + MappingMessageHandler { + handler: "handler".to_string(), + message: "message_0".to_string(), + }, + MappingMessageHandler { + handler: "handler".to_string(), + message: "message_0".to_string(), + }, + ]), + vec![duplicate_url_type("message_0")], + ), + ]; + + for (data_source, errors) in &cases { + let validation_errors = data_source.validate(); + + assert_eq!(errors.len(), validation_errors.len()); + + for error in errors.iter() { + assert!( + validation_errors + .iter() + .any(|validation_error| validation_error.to_string() == error.to_string()), + r#"expected "{}" to be in validation errors, but it wasn't"#, + error + ); + } + } + } + impl DataSource { fn with_event_handlers(event_handlers: Vec) -> DataSource { DataSource { @@ -538,6 +628,29 @@ mod tests { block_handlers: vec![], event_handlers, transaction_handlers: vec![], + message_handlers: vec![], + runtime: Arc::new(vec![]), + link: "test".to_string().into(), + }, + context: Arc::new(None), + creation_block: None, + } + } + + fn with_message_handlers(message_handlers: Vec) -> DataSource { + DataSource { + kind: "cosmos".to_string(), + network: None, + name: "Test".to_string(), + source: Source { start_block: 1 }, + mapping: Mapping { + api_version: semver::Version::new(0, 0, 0), + language: "".to_string(), + entities: vec![], + block_handlers: vec![], + event_handlers: vec![], + transaction_handlers: vec![], + message_handlers, runtime: Arc::new(vec![]), link: "test".to_string().into(), }, diff --git a/chain/cosmos/src/protobuf/sf.cosmos.r#type.v1.rs b/chain/cosmos/src/protobuf/sf.cosmos.r#type.v1.rs index d720b3980e6..1f43320e9a9 100644 --- a/chain/cosmos/src/protobuf/sf.cosmos.r#type.v1.rs +++ b/chain/cosmos/src/protobuf/sf.cosmos.r#type.v1.rs @@ -41,6 +41,8 @@ pub struct EventData { pub event: ::core::option::Option, #[prost(message, optional, tag="2")] pub block: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub tx: ::core::option::Option, } #[graph_runtime_derive::generate_asc_type(__required__{tx: TxResult,block: HeaderOnlyBlock})] #[graph_runtime_derive::generate_network_type_id(Cosmos)] @@ -52,6 +54,34 @@ pub struct TransactionData { #[prost(message, optional, tag="2")] pub block: ::core::option::Option, } +#[graph_runtime_derive::generate_asc_type(__required__{message: Any,block: HeaderOnlyBlock,tx: TransactionContext})] +#[graph_runtime_derive::generate_network_type_id(Cosmos)] +#[graph_runtime_derive::generate_from_rust_type(__required__{message: Any,block: HeaderOnlyBlock,tx: TransactionContext})] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MessageData { + #[prost(message, optional, tag="1")] + pub message: ::core::option::Option<::prost_types::Any>, + #[prost(message, optional, tag="2")] + pub block: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub tx: ::core::option::Option, +} +#[graph_runtime_derive::generate_asc_type()] +#[graph_runtime_derive::generate_network_type_id(Cosmos)] +#[graph_runtime_derive::generate_from_rust_type()] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionContext { + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + #[prost(uint32, tag="2")] + pub index: u32, + #[prost(uint32, tag="3")] + pub code: u32, + #[prost(int64, tag="4")] + pub gas_wanted: i64, + #[prost(int64, tag="5")] + pub gas_used: i64, +} #[graph_runtime_derive::generate_asc_type(__required__{last_block_id: BlockID})] #[graph_runtime_derive::generate_network_type_id(Cosmos)] #[graph_runtime_derive::generate_from_rust_type(__required__{last_block_id: BlockID})] diff --git a/chain/cosmos/src/runtime/mod.rs b/chain/cosmos/src/runtime/mod.rs index 17a8ec8f75e..77702f3ba90 100644 --- a/chain/cosmos/src/runtime/mod.rs +++ b/chain/cosmos/src/runtime/mod.rs @@ -64,6 +64,7 @@ mod test { assert_asc_bytes!(AscEventData { event: new_asc_ptr(), block: new_asc_ptr(), + tx: new_asc_ptr(), }); assert_asc_bytes!(AscTransactionData { @@ -71,6 +72,20 @@ mod test { block: new_asc_ptr(), }); + assert_asc_bytes!(AscMessageData { + message: new_asc_ptr(), + block: new_asc_ptr(), + tx: new_asc_ptr(), + }); + + assert_asc_bytes!(AscTransactionContext { + hash: new_asc_ptr(), + index: 20, + code: 20, + gas_wanted: 20, + gas_used: 20, + }); + assert_asc_bytes!(AscHeader { version: new_asc_ptr(), chain_id: new_asc_ptr(), diff --git a/chain/cosmos/src/trigger.rs b/chain/cosmos/src/trigger.rs index 35880c52e62..52a64e4b0f2 100644 --- a/chain/cosmos/src/trigger.rs +++ b/chain/cosmos/src/trigger.rs @@ -20,6 +20,7 @@ impl std::fmt::Debug for CosmosTrigger { origin: EventOrigin, }, Transaction, + Message, } let trigger_without_block = match self { @@ -29,6 +30,7 @@ impl std::fmt::Debug for CosmosTrigger { origin: *origin, }, CosmosTrigger::Transaction(_) => MappingTriggerWithoutBlock::Transaction, + CosmosTrigger::Message(_) => MappingTriggerWithoutBlock::Message, }; write!(f, "{:?}", trigger_without_block) @@ -49,6 +51,9 @@ impl ToAscPtr for CosmosTrigger { CosmosTrigger::Transaction(transaction_data) => { asc_new(heap, transaction_data.as_ref(), gas)?.erase() } + CosmosTrigger::Message(message_data) => { + asc_new(heap, message_data.as_ref(), gas)?.erase() + } }) } } @@ -61,6 +66,7 @@ pub enum CosmosTrigger { origin: EventOrigin, }, Transaction(Arc), + Message(Arc), } impl CheapClone for CosmosTrigger { @@ -74,6 +80,9 @@ impl CheapClone for CosmosTrigger { CosmosTrigger::Transaction(transaction_data) => { CosmosTrigger::Transaction(transaction_data.cheap_clone()) } + CosmosTrigger::Message(message_data) => { + CosmosTrigger::Message(message_data.cheap_clone()) + } } } } @@ -99,6 +108,7 @@ impl PartialEq for CosmosTrigger { } } (Self::Transaction(a_ptr), Self::Transaction(b_ptr)) => a_ptr == b_ptr, + (Self::Message(a_ptr), Self::Message(b_ptr)) => a_ptr == b_ptr, _ => false, } } @@ -110,12 +120,14 @@ impl CosmosTrigger { pub(crate) fn with_event( event: codec::Event, block: codec::HeaderOnlyBlock, + tx_context: Option, origin: EventOrigin, ) -> CosmosTrigger { CosmosTrigger::Event { event_data: Arc::new(codec::EventData { event: Some(event), block: Some(block), + tx: tx_context, }), origin, } @@ -131,6 +143,18 @@ impl CosmosTrigger { })) } + pub(crate) fn with_message( + message: ::prost_types::Any, + block: codec::HeaderOnlyBlock, + tx_context: codec::TransactionContext, + ) -> CosmosTrigger { + CosmosTrigger::Message(Arc::new(codec::MessageData { + message: Some(message), + block: Some(block), + tx: Some(tx_context), + })) + } + pub fn block_number(&self) -> Result { match self { CosmosTrigger::Block(block) => Ok(block.number()), @@ -138,6 +162,7 @@ impl CosmosTrigger { CosmosTrigger::Transaction(transaction_data) => { transaction_data.block().map(|b| b.number()) } + CosmosTrigger::Message(message_data) => message_data.block().map(|b| b.number()), } } @@ -148,6 +173,7 @@ impl CosmosTrigger { CosmosTrigger::Transaction(transaction_data) => { transaction_data.block().map(|b| b.hash()) } + CosmosTrigger::Message(message_data) => message_data.block().map(|b| b.hash()), } } } @@ -155,17 +181,13 @@ impl CosmosTrigger { impl Ord for CosmosTrigger { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { - // Keep the order when comparing two block triggers - (Self::Block(..), Self::Block(..)) => Ordering::Equal, - - // Block triggers always come last - (Self::Block(..), _) => Ordering::Greater, - (_, Self::Block(..)) => Ordering::Less, - // Events have no intrinsic ordering information, so we keep the order in // which they are included in the `events` field (Self::Event { .. }, Self::Event { .. }) => Ordering::Equal, + // Keep the order when comparing two message triggers + (Self::Message(..), Self::Message(..)) => Ordering::Equal, + // Transactions are ordered by their index inside the block (Self::Transaction(a), Self::Transaction(b)) => { if let (Ok(a_tx_result), Ok(b_tx_result)) = (a.tx_result(), b.tx_result()) { @@ -175,9 +197,20 @@ impl Ord for CosmosTrigger { } } - // When comparing events and transactions, transactions go first - (Self::Transaction(..), Self::Event { .. }) => Ordering::Less, - (Self::Event { .. }, Self::Transaction(..)) => Ordering::Greater, + // Keep the order when comparing two block triggers + (Self::Block(..), Self::Block(..)) => Ordering::Equal, + + // Event triggers always come first + (Self::Event { .. }, _) => Ordering::Greater, + (_, Self::Event { .. }) => Ordering::Less, + + // Block triggers always come last + (Self::Block(..), _) => Ordering::Less, + (_, Self::Block(..)) => Ordering::Greater, + + // Message triggers before Transaction triggers + (Self::Message(..), Self::Transaction(..)) => Ordering::Greater, + (Self::Transaction(..), Self::Message(..)) => Ordering::Less, } } } @@ -208,7 +241,7 @@ impl TriggerData for CosmosTrigger { event.event_type, origin, ) } else { - "event in block".to_string() + "event".to_string() } } CosmosTrigger::Transaction(transaction_data) => { @@ -222,9 +255,105 @@ impl TriggerData for CosmosTrigger { response_deliver_tx.log ) } else { - "transaction block".to_string() + "transaction".to_string() + } + } + CosmosTrigger::Message(message_data) => { + if let (Ok(message), Ok(block_number), Ok(block_hash)) = ( + message_data.message(), + self.block_number(), + self.block_hash(), + ) { + format!( + "message type {}, block #{block_number}, hash {block_hash}", + message.type_url, + ) + } else { + "message".to_string() } } } } } + +#[cfg(test)] +mod tests { + use crate::codec::TxResult; + + use super::*; + + #[test] + fn test_cosmos_trigger_ordering() { + let event_trigger = CosmosTrigger::Event { + event_data: Arc::::new(codec::EventData { + ..Default::default() + }), + origin: EventOrigin::BeginBlock, + }; + let other_event_trigger = CosmosTrigger::Event { + event_data: Arc::::new(codec::EventData { + ..Default::default() + }), + origin: EventOrigin::BeginBlock, + }; + let message_trigger = + CosmosTrigger::Message(Arc::::new(codec::MessageData { + ..Default::default() + })); + let other_message_trigger = + CosmosTrigger::Message(Arc::::new(codec::MessageData { + ..Default::default() + })); + let transaction_trigger = CosmosTrigger::Transaction(Arc::::new( + codec::TransactionData { + block: None, + tx: Some(TxResult { + index: 1, + ..Default::default() + }), + }, + )); + let other_transaction_trigger = CosmosTrigger::Transaction( + Arc::::new(codec::TransactionData { + block: None, + tx: Some(TxResult { + index: 2, + ..Default::default() + }), + }), + ); + let block_trigger = CosmosTrigger::Block(Arc::::new(codec::Block { + ..Default::default() + })); + let other_block_trigger = CosmosTrigger::Block(Arc::::new(codec::Block { + ..Default::default() + })); + + assert_eq!(event_trigger.cmp(&block_trigger), Ordering::Greater); + assert_eq!(event_trigger.cmp(&transaction_trigger), Ordering::Greater); + assert_eq!(event_trigger.cmp(&message_trigger), Ordering::Greater); + assert_eq!(event_trigger.cmp(&other_event_trigger), Ordering::Equal); + + assert_eq!(message_trigger.cmp(&block_trigger), Ordering::Greater); + assert_eq!(message_trigger.cmp(&transaction_trigger), Ordering::Greater); + assert_eq!(message_trigger.cmp(&other_message_trigger), Ordering::Equal); + assert_eq!(message_trigger.cmp(&event_trigger), Ordering::Less); + + assert_eq!(transaction_trigger.cmp(&block_trigger), Ordering::Greater); + assert_eq!( + transaction_trigger.cmp(&other_transaction_trigger), + Ordering::Less + ); + assert_eq!( + other_transaction_trigger.cmp(&transaction_trigger), + Ordering::Greater + ); + assert_eq!(transaction_trigger.cmp(&message_trigger), Ordering::Less); + assert_eq!(transaction_trigger.cmp(&event_trigger), Ordering::Less); + + assert_eq!(block_trigger.cmp(&other_block_trigger), Ordering::Equal); + assert_eq!(block_trigger.cmp(&transaction_trigger), Ordering::Less); + assert_eq!(block_trigger.cmp(&message_trigger), Ordering::Less); + assert_eq!(block_trigger.cmp(&event_trigger), Ordering::Less); + } +} diff --git a/graph/src/runtime/mod.rs b/graph/src/runtime/mod.rs index 14128e6a0b1..4b7109c6f8e 100644 --- a/graph/src/runtime/mod.rs +++ b/graph/src/runtime/mod.rs @@ -330,11 +330,12 @@ pub enum IndexForAscTypeId { CosmosValidatorSetUpdates = 1559, CosmosValidatorUpdate = 1560, CosmosVersionParams = 1561, - + CosmosMessageData = 1562, + CosmosTransactionContext = 1563, // Continue to add more Cosmos type IDs here. // e.g.: - // NextCosmosType = 1562, - // AnotherCosmosType = 1563, + // NextCosmosType = 1564, + // AnotherCosmosType = 1565, // ... // LastCosmosType = 2499, From 7cf57b03fc7c5049fa457f2084b55fc4d49ec281 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Mon, 3 Oct 2022 09:27:16 +0000 Subject: [PATCH 042/253] enable static filters for BSC (#4008) --- core/src/subgraph/instance_manager.rs | 32 +++++++++++++++++++++------ graph/src/env/mod.rs | 6 +++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/core/src/subgraph/instance_manager.rs b/core/src/subgraph/instance_manager.rs index 785f848df3d..66324983d02 100644 --- a/core/src/subgraph/instance_manager.rs +++ b/core/src/subgraph/instance_manager.rs @@ -182,7 +182,7 @@ impl SubgraphInstanceManager { // that is done store.start_subgraph_deployment(&logger).await?; - let (manifest, manifest_idx_and_name) = { + let (manifest, manifest_idx_and_name, static_data_sources) = { info!(logger, "Resolve subgraph files using IPFS"); let mut manifest = SubgraphManifest::resolve_from_raw( @@ -218,6 +218,8 @@ impl SubgraphInstanceManager { info!(logger, "Successfully resolved subgraph files using IPFS"); + let static_data_sources = manifest.data_sources.clone(); + // Add dynamic data sources to the subgraph manifest.data_sources.extend(data_sources); @@ -227,9 +229,12 @@ impl SubgraphInstanceManager { manifest.data_sources.len() ); - (manifest, manifest_idx_and_name) + (manifest, manifest_idx_and_name, static_data_sources) }; + let static_filters = + self.static_filters || manifest.data_sources.len() >= ENV_VARS.static_filters_threshold; + let onchain_data_sources = manifest .data_sources .iter() @@ -244,10 +249,20 @@ impl SubgraphInstanceManager { .with_context(|| format!("no chain configured for network {}", network))? .clone(); - // Obtain filters from the manifest - let mut filter = C::TriggerFilter::from_data_sources(onchain_data_sources.iter()); + // if static_filters is enabled, build a minimal filter with the static data sources and + // add the necessary filters based on templates. + // if not enabled we just stick to the filter based on all the data sources. + // This specifically removes dynamic data sources based filters because these can be derived + // from templates AND this reduces the cost of egress traffic by making the payloads smaller. + let filter = if static_filters { + if !self.static_filters { + info!(logger, "forcing subgraph to use static filters.") + } + + let onchain_data_sources = static_data_sources.iter().filter_map(|d| d.as_onchain()); + + let mut filter = C::TriggerFilter::from_data_sources(onchain_data_sources); - if self.static_filters { filter.extend_with_template( manifest .templates @@ -255,7 +270,10 @@ impl SubgraphInstanceManager { .filter_map(|ds| ds.as_onchain()) .cloned(), ); - } + filter + } else { + C::TriggerFilter::from_data_sources(onchain_data_sources.iter()) + }; let start_blocks = manifest.start_blocks(); @@ -346,7 +364,7 @@ impl SubgraphInstanceManager { chain, templates, unified_api_version, - static_filters: self.static_filters, + static_filters, manifest_idx_and_name, poi_version, network, diff --git a/graph/src/env/mod.rs b/graph/src/env/mod.rs index b7e1fc71071..ba21c848c6f 100644 --- a/graph/src/env/mod.rs +++ b/graph/src/env/mod.rs @@ -203,6 +203,9 @@ pub struct EnvVars { /// Set by the environment variable `EXTERNAL_WS_BASE_URL`. No default /// value is provided. pub external_ws_base_url: Option, + /// Maximum number of Dynamic Data Sources after which a Subgraph will + /// switch to using static filter. + pub static_filters_threshold: usize, } impl EnvVars { @@ -258,6 +261,7 @@ impl EnvVars { explorer_query_threshold: Duration::from_millis(inner.explorer_query_threshold_in_msec), external_http_base_url: inner.external_http_base_url, external_ws_base_url: inner.external_ws_base_url, + static_filters_threshold: inner.static_filters_threshold, }) } @@ -371,6 +375,8 @@ struct Inner { external_http_base_url: Option, #[envconfig(from = "EXTERNAL_WS_BASE_URL")] external_ws_base_url: Option, + #[envconfig(from = "GRAPH_STATIC_FILTERS_THRESHOLD", default = "10000")] + static_filters_threshold: usize, } #[derive(Clone, Debug)] From e74794660521eddb57cdaf3fc7127565376a7be2 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Mon, 3 Oct 2022 15:46:57 +0000 Subject: [PATCH 043/253] ipfs_service: Add support for folders (#3992) --- .gitignore | 1 + Cargo.lock | 211 +++++++++++++++++++++- core/Cargo.toml | 3 + core/src/polling_monitor/ipfs_service.rs | 73 ++++++-- core/src/subgraph/context.rs | 16 +- core/src/subgraph/context/instance.rs | 2 +- core/tests/fixtures/ipfs_folder/hello.txt | 1 + graph/src/components/store/mod.rs | 2 +- graph/src/data_source/offchain.rs | 12 +- graph/src/ipfs_client.rs | 161 +++++++++++++++++ tests/tests/runner.rs | 9 +- 11 files changed, 455 insertions(+), 36 deletions(-) create mode 100644 core/tests/fixtures/ipfs_folder/hello.txt diff --git a/.gitignore b/.gitignore index 874a81467c3..e7482faa0bf 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ /docker/data/ /docker/parity/chains/ /docker/parity/network/ +**/*/tests/fixtures/ipfs_folder/random.txt /tests/integration-tests/**/build /tests/integration-tests/**/generated diff --git a/Cargo.lock b/Cargo.lock index 619f5fa378e..13030c7cdf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,9 +429,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cc" @@ -478,9 +478,9 @@ checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ "core2", "multibase", - "multihash", + "multihash 0.16.2", "serde", - "unsigned-varint", + "unsigned-varint 0.7.1", ] [[package]] @@ -544,6 +544,22 @@ dependencies = [ "unreachable", ] +[[package]] +name = "common-multipart-rfc7578" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baee326bc603965b0f26583e1ecd7c111c41b49bd92a344897476a352798869" +dependencies = [ + "bytes", + "futures-core", + "futures-util", + "http", + "mime", + "mime_guess", + "rand", + "thiserror", +] + [[package]] name = "console" version = "0.13.0" @@ -1038,6 +1054,15 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -1048,6 +1073,17 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1667,6 +1703,8 @@ dependencies = [ "graph-runtime-wasm", "graphql-parser", "hex", + "ipfs-api", + "ipfs-api-backend-hyper", "lazy_static", "lru_time_cache", "pretty_assertions", @@ -1677,6 +1715,7 @@ dependencies = [ "test-store", "tower 0.4.12 (git+https://github.com/tower-rs/tower.git)", "tower-test", + "uuid 0.8.2", ] [[package]] @@ -2132,6 +2171,34 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-multipart-rfc7578" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb2cf73e96e9925f4bed948e763aa2901c2f1a3a5f713ee41917433ced6671" +dependencies = [ + "bytes", + "common-multipart-rfc7578", + "futures-core", + "http", + "hyper", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -2281,6 +2348,58 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "ipfs-api" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d4854b5cde0a7e935ee4246c5b6c67fbf4961e303963537f10e184a4830bd17" +dependencies = [ + "ipfs-api-backend-hyper", +] + +[[package]] +name = "ipfs-api-backend-hyper" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114341e043a87eff0e43ec192f0a495988323dc14c468448e62b20aaf96bd1e6" +dependencies = [ + "async-trait", + "bytes", + "futures 0.3.16", + "http", + "hyper", + "hyper-multipart-rfc7578", + "hyper-rustls", + "ipfs-api-prelude", + "thiserror", +] + +[[package]] +name = "ipfs-api-prelude" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbaf47fa129710ae041d5844a15b1365bdad8551673aee237449ba9dec6bcadc" +dependencies = [ + "async-trait", + "bytes", + "cfg-if 1.0.0", + "common-multipart-rfc7578", + "dirs", + "futures 0.3.16", + "http", + "multibase", + "parity-multiaddr", + "serde", + "serde_json", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util 0.6.7", + "tracing", + "typed-builder", + "walkdir", +] + [[package]] name = "ipnet" version = "2.3.1" @@ -2644,6 +2763,17 @@ dependencies = [ "data-encoding-macro", ] +[[package]] +name = "multihash" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dac63698b887d2d929306ea48b63760431ff8a24fac40ddb22f9c7f49fb7cab" +dependencies = [ + "generic-array", + "multihash-derive 0.7.2", + "unsigned-varint 0.5.1", +] + [[package]] name = "multihash" version = "0.16.2" @@ -2655,10 +2785,24 @@ dependencies = [ "blake3 1.3.1", "core2", "digest 0.10.5", - "multihash-derive", + "multihash-derive 0.8.0", "sha2 0.10.6", "sha3", - "unsigned-varint", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "multihash-derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "424f6e86263cd5294cbd7f1e95746b95aca0e0d66bff31e5a40d6baa87b4aa99" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] @@ -2852,6 +2996,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "parity-multiaddr" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58341485071825827b7f03cf7efd1cb21e6a709bea778fb50227fd45d2f361b4" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash 0.13.2", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint 0.7.1", + "url", +] + [[package]] name = "parity-scale-codec" version = "3.0.0" @@ -3614,6 +3776,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.19" @@ -4665,6 +4836,17 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typed-builder" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.15.0" @@ -4743,6 +4925,12 @@ dependencies = [ "void", ] +[[package]] +name = "unsigned-varint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fdeedbf205afadfe39ae559b75c3240f24e257d0ca27e85f85cb82aa19ac35" + [[package]] name = "unsigned-varint" version = "0.7.1" @@ -4808,6 +4996,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index db121acb844..44aa8965e90 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -38,3 +38,6 @@ hex = "0.4.3" graphql-parser = "0.4.0" pretty_assertions = "1.3.0" anyhow = "1.0" +ipfs-api-backend-hyper = "0.5" +ipfs-api = { version = "0.16.0", features = ["with-hyper-rustls"], default-features = false } +uuid = { version = "0.8.1", features = ["v4"] } diff --git a/core/src/polling_monitor/ipfs_service.rs b/core/src/polling_monitor/ipfs_service.rs index 05d904f17fc..856910130a0 100644 --- a/core/src/polling_monitor/ipfs_service.rs +++ b/core/src/polling_monitor/ipfs_service.rs @@ -1,10 +1,9 @@ use anyhow::{anyhow, Error}; use bytes::Bytes; -use cid::Cid; use futures::{Future, FutureExt}; use graph::{ cheap_clone::CheapClone, - ipfs_client::{IpfsClient, StatApi}, + ipfs_client::{CidFile, IpfsClient, StatApi}, tokio::sync::Semaphore, }; use std::{pin::Pin, sync::Arc, task::Poll, time::Duration}; @@ -48,16 +47,21 @@ impl IpfsService { } } - async fn call(&self, cid: Cid) -> Result, Error> { + async fn call(&self, req: &CidFile) -> Result, Error> { + let CidFile { cid, path } = req; let multihash = cid.hash().code(); if !SAFE_MULTIHASHES.contains(&multihash) { return Err(anyhow!("CID multihash {} is not allowed", multihash)); } - let cid_str = cid.to_string(); + let cid_str = match path { + Some(path) => format!("{}/{}", cid, path), + None => cid.to_string(), + }; + let size = match self .client - .stat_size(StatApi::Files, cid_str, self.timeout) + .stat_size(StatApi::Files, cid_str.clone(), self.timeout) .await { Ok(size) => size, @@ -71,7 +75,7 @@ impl IpfsService { if size > self.max_file_size { return Err(anyhow!( "IPFS file {} is too large. It can be at most {} bytes but is {} bytes", - cid.to_string(), + cid_str, self.max_file_size, size )); @@ -79,15 +83,15 @@ impl IpfsService { Ok(self .client - .cat_all(&cid.to_string(), self.timeout) + .cat_all(&cid_str, self.timeout) .await .map(Some)?) } } -impl Service for IpfsService { - type Response = (Cid, Option); - type Error = (Cid, Error); +impl Service for IpfsService { + type Response = (CidFile, Option); + type Error = (CidFile, Error); type Future = Pin> + Send>>; fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { @@ -99,11 +103,14 @@ impl Service for IpfsService { .map_err(|_| unreachable!("semaphore is never closed")) } - fn call(&mut self, cid: Cid) -> Self::Future { + fn call(&mut self, req: CidFile) -> Self::Future { let this = self.cheap_clone(); async move { let _permit = this.concurrency_limiter.acquire().await; - this.call(cid).await.map(|x| (cid, x)).map_err(|e| (cid, e)) + this.call(&req) + .await + .map(|x| (req.clone(), x)) + .map_err(|e| (req.clone(), e)) } .boxed() } @@ -129,3 +136,45 @@ const SAFE_MULTIHASHES: [u64; 15] = [ 0xb260, // BLAKE2s-256 (32-byte hash size) 0x1e, // BLAKE3-256 (32-byte hash size) ]; + +#[cfg(test)] +mod test { + use ipfs::IpfsApi; + use ipfs_api as ipfs; + use std::{fs, str::FromStr, time::Duration}; + + use cid::Cid; + use graph::{ipfs_client::IpfsClient, tokio}; + + use super::IpfsService; + use uuid::Uuid; + + #[tokio::test] + async fn cat_file_in_folder() { + let path = "./tests/fixtures/ipfs_folder"; + let uid = Uuid::new_v4().to_string(); + fs::write(format!("{}/random.txt", path), &uid).unwrap(); + + let cl: ipfs::IpfsClient = ipfs::IpfsClient::default(); + + let rsp = cl.add_path(&path).await.unwrap(); + + let ipfs_folder = rsp.iter().find(|rsp| rsp.name == "ipfs_folder").unwrap(); + + let local = IpfsClient::localhost(); + let cid = Cid::from_str(&ipfs_folder.hash).unwrap(); + let file = "random.txt".to_string(); + + let svc = IpfsService::new(local, 100000, Duration::from_secs(5), 10); + + let content = svc + .call(&super::CidFile { + cid, + path: Some(file), + }) + .await + .unwrap() + .unwrap(); + assert_eq!(content.to_vec(), uid.as_bytes().to_vec()); + } +} diff --git a/core/src/subgraph/context.rs b/core/src/subgraph/context.rs index 653d6c8875f..0abb6fa6a59 100644 --- a/core/src/subgraph/context.rs +++ b/core/src/subgraph/context.rs @@ -5,7 +5,6 @@ use crate::polling_monitor::{ }; use anyhow::{self, Error}; use bytes::Bytes; -use cid::Cid; use graph::{ blockchain::Blockchain, components::{ @@ -13,6 +12,7 @@ use graph::{ subgraph::{MappingError, SharedProofOfIndexing}, }, data_source::{offchain, DataSource, TriggerData}, + ipfs_client::CidFile, prelude::{ BlockNumber, BlockState, CancelGuard, DeploymentHash, MetricsRegistry, RuntimeHostBuilder, SubgraphInstanceMetrics, TriggerProcessor, @@ -134,7 +134,7 @@ impl> IndexingContext { if host.is_some() { if let Some(source) = source { - self.offchain_monitor.add_source(&source)?; + self.offchain_monitor.add_source(source)?; } } @@ -143,8 +143,8 @@ impl> IndexingContext { } pub(crate) struct OffchainMonitor { - ipfs_monitor: PollingMonitor, - ipfs_monitor_rx: mpsc::Receiver<(Cid, Bytes)>, + ipfs_monitor: PollingMonitor, + ipfs_monitor_rx: mpsc::Receiver<(CidFile, Bytes)>, } impl OffchainMonitor { @@ -167,9 +167,9 @@ impl OffchainMonitor { } } - fn add_source(&mut self, source: &offchain::Source) -> Result<(), Error> { + fn add_source(&mut self, source: offchain::Source) -> Result<(), Error> { match source { - offchain::Source::Ipfs(cid) => self.ipfs_monitor.monitor(cid.clone()), + offchain::Source::Ipfs(cid_file) => self.ipfs_monitor.monitor(cid_file), }; Ok(()) } @@ -180,8 +180,8 @@ impl OffchainMonitor { let mut triggers = vec![]; loop { match self.ipfs_monitor_rx.try_recv() { - Ok((cid, data)) => triggers.push(offchain::TriggerData { - source: offchain::Source::Ipfs(cid), + Ok((cid_file, data)) => triggers.push(offchain::TriggerData { + source: offchain::Source::Ipfs(cid_file), data: Arc::new(data), }), Err(TryRecvError::Disconnected) => { diff --git a/core/src/subgraph/context/instance.rs b/core/src/subgraph/context/instance.rs index a9e3ade7778..3bb5d4d909c 100644 --- a/core/src/subgraph/context/instance.rs +++ b/core/src/subgraph/context/instance.rs @@ -67,7 +67,7 @@ where }; if let DataSource::Offchain(ds) = &ds { - offchain_monitor.add_source(&ds.source)?; + offchain_monitor.add_source(ds.source.clone())?; } let host = this.new_host(logger.cheap_clone(), ds, module_bytes)?; diff --git a/core/tests/fixtures/ipfs_folder/hello.txt b/core/tests/fixtures/ipfs_folder/hello.txt new file mode 100644 index 00000000000..3b18e512dba --- /dev/null +++ b/core/tests/fixtures/ipfs_folder/hello.txt @@ -0,0 +1 @@ +hello world diff --git a/graph/src/components/store/mod.rs b/graph/src/components/store/mod.rs index 77cb3635e94..69209b7f1b0 100644 --- a/graph/src/components/store/mod.rs +++ b/graph/src/components/store/mod.rs @@ -538,7 +538,7 @@ pub enum EntityChange { impl EntityChange { pub fn for_data(subgraph_id: DeploymentHash, key: EntityKey) -> Self { Self::Data { - subgraph_id: subgraph_id, + subgraph_id, entity_type: key.entity_type, } } diff --git a/graph/src/data_source/offchain.rs b/graph/src/data_source/offchain.rs index a36ca1f3fa1..d34c0dd544c 100644 --- a/graph/src/data_source/offchain.rs +++ b/graph/src/data_source/offchain.rs @@ -7,10 +7,10 @@ use crate::{ }, data::store::scalar::Bytes, data_source, + ipfs_client::CidFile, prelude::{DataSourceContext, Link}, }; use anyhow::{self, Context, Error}; -use cid::Cid; use serde::Deserialize; use slog::{info, Logger}; use std::{fmt, sync::Arc}; @@ -74,7 +74,7 @@ impl DataSource { pub fn as_stored_dynamic_data_source(&self) -> StoredDynamicDataSource { let param = match self.source { - Source::Ipfs(link) => Bytes::from(link.to_bytes()), + Source::Ipfs(ref link) => Bytes::from(link.to_bytes()), }; let context = self .context @@ -95,7 +95,9 @@ impl DataSource { stored: StoredDynamicDataSource, ) -> Result { let param = stored.param.context("no param on stored data source")?; - let source = Source::Ipfs(Cid::try_from(param.as_slice().to_vec())?); + let cid_file = CidFile::try_from(param)?; + + let source = Source::Ipfs(cid_file); let context = Arc::new(stored.context.map(serde_json::from_value).transpose()?); Ok(Self { kind: template.kind.clone(), @@ -112,14 +114,14 @@ impl DataSource { /// used as the value to be returned to mappings from the `dataSource.address()` host function. pub fn address(&self) -> Option> { match self.source { - Source::Ipfs(cid) => Some(cid.to_bytes()), + Source::Ipfs(ref cid) => Some(cid.to_bytes()), } } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum Source { - Ipfs(Cid), + Ipfs(CidFile), } #[derive(Clone, Debug)] diff --git a/graph/src/ipfs_client.rs b/graph/src/ipfs_client.rs index c99d83b1cb7..43f4f733bf7 100644 --- a/graph/src/ipfs_client.rs +++ b/graph/src/ipfs_client.rs @@ -1,14 +1,76 @@ use crate::prelude::CheapClone; +use anyhow::anyhow; use anyhow::Error; use bytes::Bytes; +use cid::Cid; use futures03::Stream; use http::header::CONTENT_LENGTH; use http::Uri; use reqwest::multipart; use serde::Deserialize; +use std::fmt::Display; use std::time::Duration; use std::{str::FromStr, sync::Arc}; +/// Represents a file on Ipfs. This file can be the CID or a path within a folder CID. +/// The path cannot have a prefix (ie CID/hello.json would be cid: CID path: "hello.json") +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct CidFile { + pub cid: Cid, + pub path: Option, +} + +impl Display for CidFile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self.path { + Some(ref f) => format!("{}/{}", self.cid.to_string(), f), + None => self.cid.to_string(), + }; + f.write_str(&str) + } +} + +impl CidFile { + pub fn to_bytes(&self) -> Vec { + self.to_string().as_bytes().to_vec() + } +} + +impl TryFrom for CidFile { + type Error = anyhow::Error; + + fn try_from(value: crate::data::store::scalar::Bytes) -> Result { + let str = String::from_utf8(value.to_vec())?; + + Self::from_str(&str) + } +} + +/// The string should not have a prefix and only one slash after the CID is removed, everything +/// else is considered a file path. If this is malformed, it will fail to find the file. +impl FromStr for CidFile { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + if s.is_empty() { + return Err(anyhow!("cid can't be empty")); + } + + let cid_str: String = s.chars().take_while(|c| *c != '/').collect(); + let cid = Cid::from_str(&cid_str)?; + + // if cid was the only content or if it's just slash terminated. + if cid_str.len() == s.len() || s.len() + 1 == cid_str.len() { + return Ok(CidFile { cid, path: None }); + } + + let file: String = (&s[cid_str.len() + 1..]).to_string(); + let path = if file.is_empty() { None } else { Some(file) }; + + Ok(CidFile { cid, path }) + } +} + #[derive(Clone, Copy, PartialEq, Eq)] pub enum StatApi { Block, @@ -167,3 +229,102 @@ impl IpfsClient { .and_then(|x| x) } } + +#[cfg(test)] +mod test { + use std::str::FromStr; + + use anyhow::anyhow; + use cid::Cid; + + use crate::ipfs_client::CidFile; + + #[test] + fn test_cid_parsing() { + let cid_str = "bafyreibjo4xmgaevkgud7mbifn3dzp4v4lyaui4yvqp3f2bqwtxcjrdqg4"; + let cid = Cid::from_str(cid_str).unwrap(); + + struct Case<'a> { + name: &'a str, + input: String, + path: String, + expected: Result, + } + + let cases = vec![ + Case { + name: "correct no slashes, no file", + input: cid_str.to_string(), + path: cid_str.to_string(), + expected: Ok(CidFile { + cid: cid.clone(), + path: None, + }), + }, + Case { + name: "correct with file path", + input: format!("{}/file.json", cid), + path: format!("{}/file.json", cid_str), + expected: Ok(CidFile { + cid: cid.clone(), + path: Some("file.json".into()), + }), + }, + Case { + name: "correct cid with trailing slash", + input: format!("{}/", cid), + path: format!("{}", cid), + expected: Ok(CidFile { + cid: cid.clone(), + path: None, + }), + }, + Case { + name: "incorrect, empty", + input: "".to_string(), + path: "".to_string(), + expected: Err(anyhow!("cid can't be empty")), + }, + Case { + name: "correct, two slahes", + input: format!("{}//", cid), + path: format!("{}//", cid), + expected: Ok(CidFile { + cid: cid.clone(), + path: Some("/".into()), + }), + }, + Case { + name: "incorrect, leading slahes", + input: format!("/ipfs/{}/file.json", cid), + path: "".to_string(), + expected: Err(anyhow!("Input too short")), + }, + Case { + name: "correct syntax, invalid CID", + input: "notacid/file.json".to_string(), + path: "".to_string(), + expected: Err(anyhow!("Failed to parse multihash")), + }, + ]; + + for case in cases { + let f = CidFile::from_str(&case.input); + + match case.expected { + Ok(cid_file) => { + assert!(f.is_ok(), "case: {}", case.name); + let f = f.unwrap(); + assert_eq!(f, cid_file, "case: {}", case.name); + assert_eq!(f.to_string(), case.path, "case: {}", case.name); + } + Err(err) => assert_eq!( + f.unwrap_err().to_string(), + err.to_string(), + "case: {}", + case.name + ), + } + } + } +} diff --git a/tests/tests/runner.rs b/tests/tests/runner.rs index 8339c8aa841..b2b132318cc 100644 --- a/tests/tests/runner.rs +++ b/tests/tests/runner.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use cid::Cid; use graph::blockchain::{Block, BlockPtr}; use graph::env::EnvVars; +use graph::ipfs_client::CidFile; use graph::object; use graph::prelude::ethabi::ethereum_types::H256; use graph::prelude::{SubgraphAssignmentProvider, SubgraphName}; @@ -147,9 +148,11 @@ async fn file_data_sources() { let id = format!( "0x{}", hex::encode( - Cid::try_from("QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ") - .unwrap() - .to_bytes(), + CidFile { + cid: Cid::try_from("QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ").unwrap(), + path: None, + } + .to_bytes() ) ); From 3b3b458ecabffcc1006a82bacf2df7f17f2859e5 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Mon, 3 Oct 2022 18:47:15 +0100 Subject: [PATCH 044/253] fix: Allow grafts to add data sources (#3989) --- core/src/subgraph/instance_manager.rs | 76 +++++++++-------- core/src/subgraph/loader.rs | 2 +- core/src/subgraph/registrar.rs | 3 +- graph/src/components/store/traits.rs | 7 ++ graph/src/data/subgraph/mod.rs | 77 ++++++++++------- graph/src/data/subgraph/schema.rs | 43 +++++++++- graph/src/util/error.rs | 9 ++ graphql/tests/query.rs | 2 +- .../down.sql | 1 + .../up.sql | 1 + store/postgres/src/copy.rs | 8 ++ store/postgres/src/deployment.rs | 19 +++++ store/postgres/src/deployment_store.rs | 22 +++++ store/postgres/src/detail.rs | 2 + store/postgres/src/dynds/private.rs | 84 ++++++++++++++----- store/postgres/src/subgraph_store.rs | 19 ++++- store/postgres/tests/graft.rs | 5 +- store/postgres/tests/store.rs | 5 +- store/postgres/tests/subgraph.rs | 2 +- store/postgres/tests/writable.rs | 2 +- store/test-store/src/store.rs | 5 +- .../data-source-revert/grafted.yaml | 20 +++++ .../integration-tests/typename/subgraph.yaml | 2 +- 23 files changed, 312 insertions(+), 104 deletions(-) create mode 100644 store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/down.sql create mode 100644 store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/up.sql diff --git a/core/src/subgraph/instance_manager.rs b/core/src/subgraph/instance_manager.rs index 66324983d02..5a575bdd27c 100644 --- a/core/src/subgraph/instance_manager.rs +++ b/core/src/subgraph/instance_manager.rs @@ -8,7 +8,7 @@ use graph::blockchain::Blockchain; use graph::blockchain::NodeCapabilities; use graph::blockchain::{BlockchainKind, TriggerFilter}; use graph::components::subgraph::ProofOfIndexingVersion; -use graph::data::subgraph::SPEC_VERSION_0_0_6; +use graph::data::subgraph::{UnresolvedSubgraphManifest, SPEC_VERSION_0_0_6}; use graph::prelude::{SubgraphInstanceManager as SubgraphInstanceManagerTrait, *}; use graph::{blockchain::BlockchainMap, components::store::DeploymentLocator}; use graph_runtime_wasm::module::ToAscPtr; @@ -176,47 +176,49 @@ impl SubgraphInstanceManager { .writable(logger.clone(), deployment.id) .await?; + let raw_yaml = serde_yaml::to_string(&manifest).unwrap(); + let manifest = UnresolvedSubgraphManifest::parse(deployment.hash.cheap_clone(), manifest)?; + + // Make sure the `raw_yaml` is present on both this subgraph and the graft base. + self.subgraph_store + .set_manifest_raw_yaml(&deployment.hash, raw_yaml) + .await?; + if let Some(graft) = &manifest.graft { + let file_bytes = self + .link_resolver + .cat(&logger, &graft.base.to_ipfs_link()) + .await?; + let yaml = String::from_utf8(file_bytes)?; + self.subgraph_store + .set_manifest_raw_yaml(&graft.base, yaml) + .await?; + } + + info!(logger, "Resolve subgraph files using IPFS"); + + // Allow for infinite retries for subgraph definition files. + let link_resolver = Arc::from(self.link_resolver.with_retries()); + let mut manifest = manifest + .resolve(&link_resolver, &logger, ENV_VARS.max_spec_version.clone()) + .await?; + + info!(logger, "Successfully resolved subgraph files using IPFS"); + + let manifest_idx_and_name: Vec<(u32, String)> = manifest.template_idx_and_name().collect(); + // Start the subgraph deployment before reading dynamic data // sources; if the subgraph is a graft or a copy, starting it will // do the copying and dynamic data sources won't show up until after // that is done store.start_subgraph_deployment(&logger).await?; - let (manifest, manifest_idx_and_name, static_data_sources) = { - info!(logger, "Resolve subgraph files using IPFS"); - - let mut manifest = SubgraphManifest::resolve_from_raw( - deployment.hash.cheap_clone(), - manifest, - // Allow for infinite retries for subgraph definition files. - &Arc::from(self.link_resolver.with_retries()), - &logger, - ENV_VARS.max_spec_version.clone(), - ) - .await - .context("Failed to resolve subgraph from IPFS")?; - - // We cannot include static data sources in the map because a static data source and a - // template may have the same name in the manifest. - let ds_len = manifest.data_sources.len() as u32; - let manifest_idx_and_name: Vec<(u32, String)> = manifest - .templates - .iter() - .map(|t| t.name().to_owned()) - .enumerate() - .map(|(idx, name)| (ds_len + idx as u32, name)) - .collect(); - - let data_sources = load_dynamic_data_sources( - store.clone(), - logger.clone(), - &manifest, - manifest_idx_and_name.clone(), - ) - .await - .context("Failed to load dynamic data sources")?; - - info!(logger, "Successfully resolved subgraph files using IPFS"); + // Dynamic data sources are loaded by appending them to the manifest. + // + // Refactor: Preferrably we'd avoid any mutation of the manifest. + let (manifest, static_data_sources) = { + let data_sources = load_dynamic_data_sources(store.clone(), logger.clone(), &manifest) + .await + .context("Failed to load dynamic data sources")?; let static_data_sources = manifest.data_sources.clone(); @@ -229,7 +231,7 @@ impl SubgraphInstanceManager { manifest.data_sources.len() ); - (manifest, manifest_idx_and_name, static_data_sources) + (manifest, static_data_sources) }; let static_filters = diff --git a/core/src/subgraph/loader.rs b/core/src/subgraph/loader.rs index 3e5931984db..6b302f58b07 100644 --- a/core/src/subgraph/loader.rs +++ b/core/src/subgraph/loader.rs @@ -9,8 +9,8 @@ pub async fn load_dynamic_data_sources( store: Arc, logger: Logger, manifest: &SubgraphManifest, - manifest_idx_and_name: Vec<(u32, String)>, ) -> Result>, Error> { + let manifest_idx_and_name = manifest.template_idx_and_name().collect(); let start_time = Instant::now(); let mut data_sources: Vec> = vec![]; diff --git a/core/src/subgraph/registrar.rs b/core/src/subgraph/registrar.rs index 8c4a2156a3e..8fb8c34af86 100644 --- a/core/src/subgraph/registrar.rs +++ b/core/src/subgraph/registrar.rs @@ -552,6 +552,7 @@ async fn create_subgraph_version( version_switching_mode: SubgraphVersionSwitchingMode, resolver: &Arc, ) -> Result { + let raw_string = serde_yaml::to_string(&raw).unwrap(); let unvalidated = UnvalidatedSubgraphManifest::::resolve( deployment, raw, @@ -618,7 +619,7 @@ async fn create_subgraph_version( // Apply the subgraph versioning and deployment operations, // creating a new subgraph deployment if one doesn't exist. - let deployment = DeploymentCreate::new(&manifest, start_block) + let deployment = DeploymentCreate::new(raw_string, &manifest, start_block) .graft(base_block) .debug(debug_fork); deployment_store diff --git a/graph/src/components/store/traits.rs b/graph/src/components/store/traits.rs index 1749b8bea09..087bde3c32a 100644 --- a/graph/src/components/store/traits.rs +++ b/graph/src/components/store/traits.rs @@ -152,6 +152,13 @@ pub trait SubgraphStore: Send + Sync + 'static { /// Find the deployment locators for the subgraph with the given hash fn locators(&self, hash: &str) -> Result, StoreError>; + + /// This migrates subgraphs that existed before the raw_yaml column was added. + async fn set_manifest_raw_yaml( + &self, + hash: &DeploymentHash, + raw_yaml: String, + ) -> Result<(), StoreError>; } pub trait ReadStore: Send + Sync + 'static { diff --git a/graph/src/data/subgraph/mod.rs b/graph/src/data/subgraph/mod.rs index 3e451eb6a87..e6f0924203d 100644 --- a/graph/src/data/subgraph/mod.rs +++ b/graph/src/data/subgraph/mod.rs @@ -10,13 +10,12 @@ pub mod status; pub use features::{SubgraphFeature, SubgraphFeatureValidationError}; -use anyhow::ensure; use anyhow::{anyhow, Error}; use futures03::{future::try_join3, stream::FuturesOrdered, TryStreamExt as _}; use semver::Version; use serde::{de, ser}; use serde_yaml; -use slog::{debug, info, Logger}; +use slog::{info, Logger}; use stable_hash::{FieldAddress, StableHash}; use stable_hash_legacy::SequenceNumber; use std::{collections::BTreeSet, marker::PhantomData}; @@ -25,6 +24,7 @@ use wasmparser; use web3::types::Address; use crate::{ + bail, blockchain::{BlockPtr, Blockchain, DataSource as _}, components::{ link_resolver::LinkResolver, @@ -41,6 +41,7 @@ use crate::{ offchain::OFFCHAIN_KINDS, DataSource, DataSourceTemplate, UnresolvedDataSource, UnresolvedDataSourceTemplate, }, + ensure, prelude::{r, CheapClone, ENV_VARS}, }; @@ -356,7 +357,7 @@ pub enum SubgraphManifestResolveError { #[error("subgraph is not valid YAML")] InvalidFormat, #[error("resolve error: {0}")] - ResolveError(anyhow::Error), + ResolveError(#[from] anyhow::Error), } /// Data source contexts are conveniently represented as entities. @@ -496,7 +497,7 @@ pub struct BaseSubgraphManifest { } /// SubgraphManifest with IPFS links unresolved -type UnresolvedSubgraphManifest = BaseSubgraphManifest< +pub type UnresolvedSubgraphManifest = BaseSubgraphManifest< C, UnresolvedSchema, UnresolvedDataSource, @@ -614,35 +615,16 @@ impl SubgraphManifest { /// Entry point for resolving a subgraph definition. pub async fn resolve_from_raw( id: DeploymentHash, - mut raw: serde_yaml::Mapping, + raw: serde_yaml::Mapping, resolver: &Arc, logger: &Logger, max_spec_version: semver::Version, ) -> Result { - // Inject the IPFS hash as the ID of the subgraph into the definition. - raw.insert("id".into(), id.to_string().into()); - - // Parse the YAML data into an UnresolvedSubgraphManifest - let unresolved: UnresolvedSubgraphManifest = serde_yaml::from_value(raw.into())?; - - debug!(logger, "Features {:?}", unresolved.features); + let unresolved = UnresolvedSubgraphManifest::parse(id, raw)?; let resolved = unresolved .resolve(resolver, logger, max_spec_version) - .await - .map_err(SubgraphManifestResolveError::ResolveError)?; - - if (resolved.spec_version < SPEC_VERSION_0_0_7) - && resolved - .data_sources - .iter() - .any(|ds| OFFCHAIN_KINDS.contains(&ds.kind())) - { - return Err(SubgraphManifestResolveError::ResolveError(anyhow!( - "Offchain data sources not supported prior to {}", - SPEC_VERSION_0_0_7 - ))); - } + .await?; Ok(resolved) } @@ -685,15 +667,37 @@ impl SubgraphManifest { ) -> Result { UnifiedMappingApiVersion::try_from_versions(self.api_versions()) } + + pub fn template_idx_and_name(&self) -> impl Iterator + '_ { + // We cannot include static data sources in the map because a static data source and a + // template may have the same name in the manifest. Duplicated with + // `UnresolvedSubgraphManifest::template_idx_and_name`. + let ds_len = self.data_sources.len() as u32; + self.templates + .iter() + .map(|t| t.name().to_owned()) + .enumerate() + .map(move |(idx, name)| (ds_len + idx as u32, name)) + } } impl UnresolvedSubgraphManifest { + pub fn parse( + id: DeploymentHash, + mut raw: serde_yaml::Mapping, + ) -> Result { + // Inject the IPFS hash as the ID of the subgraph into the definition. + raw.insert("id".into(), id.to_string().into()); + + serde_yaml::from_value(raw.into()).map_err(Into::into) + } + pub async fn resolve( self, resolver: &Arc, logger: &Logger, max_spec_version: semver::Version, - ) -> Result, anyhow::Error> { + ) -> Result, SubgraphManifestResolveError> { let UnresolvedSubgraphManifest { id, spec_version, @@ -714,14 +718,14 @@ impl UnresolvedSubgraphManifest { max_spec_version, id, spec_version - )); + ).into()); } let ds_count = data_sources.len(); if ds_count as u64 + templates.len() as u64 > u32::MAX as u64 { - return Err(anyhow!( - "Subgraph has too many declared data sources and templates", - )); + return Err( + anyhow!("Subgraph has too many declared data sources and templates",).into(), + ); } let (schema, data_sources, templates) = try_join3( @@ -754,6 +758,17 @@ impl UnresolvedSubgraphManifest { ); } + if spec_version < SPEC_VERSION_0_0_7 + && data_sources + .iter() + .any(|ds| OFFCHAIN_KINDS.contains(&ds.kind())) + { + bail!( + "Offchain data sources not supported prior to {}", + SPEC_VERSION_0_0_7 + ); + } + Ok(SubgraphManifest { id, spec_version, diff --git a/graph/src/data/subgraph/schema.rs b/graph/src/data/subgraph/schema.rs index c8e5d7f80df..81c3ffa5586 100644 --- a/graph/src/data/subgraph/schema.rs +++ b/graph/src/data/subgraph/schema.rs @@ -1,6 +1,6 @@ //! Entity types that contain the graph-node state. -use anyhow::{anyhow, Error}; +use anyhow::{anyhow, bail, Error}; use hex; use lazy_static::lazy_static; use rand::rngs::OsRng; @@ -110,11 +110,12 @@ pub struct DeploymentCreate { impl DeploymentCreate { pub fn new( + raw_manifest: String, source_manifest: &SubgraphManifest, earliest_block: Option, ) -> Self { Self { - manifest: SubgraphManifestEntity::from(source_manifest), + manifest: SubgraphManifestEntity::new(raw_manifest, source_manifest), earliest_block: earliest_block.cheap_clone(), graft_base: None, graft_block: None, @@ -163,18 +164,52 @@ pub struct SubgraphManifestEntity { pub repository: Option, pub features: Vec, pub schema: String, + pub raw_yaml: Option, } -impl<'a, C: Blockchain> From<&'a super::SubgraphManifest> for SubgraphManifestEntity { - fn from(manifest: &'a super::SubgraphManifest) -> Self { +impl SubgraphManifestEntity { + pub fn new(raw_yaml: String, manifest: &super::SubgraphManifest) -> Self { Self { spec_version: manifest.spec_version.to_string(), description: manifest.description.clone(), repository: manifest.repository.clone(), features: manifest.features.iter().map(|f| f.to_string()).collect(), schema: manifest.schema.document.clone().to_string(), + raw_yaml: Some(raw_yaml), } } + + pub fn template_idx_and_name(&self) -> Result, Error> { + #[derive(Debug, Deserialize)] + struct MinimalDs { + name: String, + } + #[derive(Debug, Deserialize)] + #[serde(rename_all = "camelCase")] + struct MinimalManifest { + data_sources: Vec, + #[serde(default)] + templates: Vec, + } + + let raw_yaml = match &self.raw_yaml { + Some(raw_yaml) => raw_yaml, + None => bail!("raw_yaml not present"), + }; + + let manifest: MinimalManifest = serde_yaml::from_str(raw_yaml)?; + + let ds_len = manifest.data_sources.len() as i32; + let template_idx_and_name = manifest + .templates + .iter() + .map(|t| t.name.to_owned()) + .enumerate() + .map(move |(idx, name)| (ds_len + idx as i32, name)) + .collect(); + + Ok(template_idx_and_name) + } } #[derive(Clone, Debug)] diff --git a/graph/src/util/error.rs b/graph/src/util/error.rs index 1b3cb884467..4da4586469f 100644 --- a/graph/src/util/error.rs +++ b/graph/src/util/error.rs @@ -17,3 +17,12 @@ macro_rules! ensure { } }; } + +// `bail!` from `anyhow`, but calling `from`. +// For context see https://github.com/dtolnay/anyhow/issues/112#issuecomment-704549251. +#[macro_export] +macro_rules! bail { + ($($err:tt)*) => { + return Err(anyhow::anyhow!($($err)*).into()); + }; +} diff --git a/graphql/tests/query.rs b/graphql/tests/query.rs index 0c0b01da4ac..9365fd22044 100644 --- a/graphql/tests/query.rs +++ b/graphql/tests/query.rs @@ -267,7 +267,7 @@ async fn insert_test_entities( manifest: SubgraphManifest, id_type: IdType, ) -> DeploymentLocator { - let deployment = DeploymentCreate::new(&manifest, None); + let deployment = DeploymentCreate::new(String::new(), &manifest, None); let name = SubgraphName::new(manifest.id.as_str()).unwrap(); let node_id = NodeId::new("test").unwrap(); let deployment = store diff --git a/store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/down.sql b/store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/down.sql new file mode 100644 index 00000000000..0a8ca06be1e --- /dev/null +++ b/store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/down.sql @@ -0,0 +1 @@ +alter table subgraphs.subgraph_manifest drop column raw_yaml; diff --git a/store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/up.sql b/store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/up.sql new file mode 100644 index 00000000000..c32f3e646c1 --- /dev/null +++ b/store/postgres/migrations/2022-09-19-161239_add-raw-manifest-column/up.sql @@ -0,0 +1 @@ +alter table subgraphs.subgraph_manifest add column raw_yaml text; diff --git a/store/postgres/src/copy.rs b/store/postgres/src/copy.rs index 6454e8c04e0..d2fddbe8f93 100644 --- a/store/postgres/src/copy.rs +++ b/store/postgres/src/copy.rs @@ -672,6 +672,8 @@ pub struct Connection { src: Arc, dst: Arc, target_block: BlockPtr, + src_manifest_idx_and_name: Vec<(i32, String)>, + dst_manifest_idx_and_name: Vec<(i32, String)>, } impl Connection { @@ -687,6 +689,8 @@ impl Connection { src: Arc, dst: Arc, target_block: BlockPtr, + src_manifest_idx_and_name: Vec<(i32, String)>, + dst_manifest_idx_and_name: Vec<(i32, String)>, ) -> Result { let logger = logger.new(o!("dst" => dst.site.namespace.to_string())); @@ -712,6 +716,8 @@ impl Connection { src, dst, target_block, + src_manifest_idx_and_name, + dst_manifest_idx_and_name, }) } @@ -728,6 +734,8 @@ impl Connection { &self.conn, &DataSourcesTable::new(state.dst.site.namespace.clone()), state.target_block.number, + &self.src_manifest_idx_and_name, + &self.dst_manifest_idx_and_name, )?; } Ok(()) diff --git a/store/postgres/src/deployment.rs b/store/postgres/src/deployment.rs index 1c8f31c384d..b8b3fdff1c3 100644 --- a/store/postgres/src/deployment.rs +++ b/store/postgres/src/deployment.rs @@ -113,6 +113,7 @@ table! { /// Parent of the smallest start block from the manifest start_block_number -> Nullable, start_block_hash -> Nullable, + raw_yaml -> Nullable, } } @@ -275,6 +276,22 @@ pub fn features(conn: &PgConnection, site: &Site) -> Result Result<(), StoreError> { + use subgraph_manifest as sm; + + update(sm::table.filter(sm::id.eq(site.id))) + .filter(sm::raw_yaml.is_null()) + .set(sm::raw_yaml.eq(raw_yaml)) + .execute(conn) + .map(|_| ()) + .map_err(|e| e.into()) +} + pub fn transact_block( conn: &PgConnection, site: &Site, @@ -889,6 +906,7 @@ pub fn create_deployment( repository, features, schema, + raw_yaml, }, earliest_block, graft_base, @@ -932,6 +950,7 @@ pub fn create_deployment( m::use_bytea_prefix.eq(true), m::start_block_hash.eq(b(&earliest_block)), m::start_block_number.eq(earliest_block_number), + m::raw_yaml.eq(raw_yaml), ); if exists && replace { diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index 188b138df89..34f6669fca5 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -1316,6 +1316,15 @@ impl DeploymentStore { dst.catalog.site.namespace ); + let src_manifest_idx_and_name = self + .load_deployment(&src.site)? + .manifest + .template_idx_and_name()?; + let dst_manifest_idx_and_name = self + .load_deployment(&dst.site)? + .manifest + .template_idx_and_name()?; + // Copy subgraph data // We allow both not copying tables at all from the source, as well // as adding new tables in `self`; we only need to check that tables @@ -1327,6 +1336,8 @@ impl DeploymentStore { src.clone(), dst.clone(), block.clone(), + src_manifest_idx_and_name, + dst_manifest_idx_and_name, )?; let status = copy_conn.copy_data()?; if status == crate::copy::Status::Cancelled { @@ -1587,6 +1598,17 @@ impl DeploymentStore { self.with_conn(move |conn, _| deployment::health(conn, id).map_err(Into::into)) .await } + + pub(crate) async fn set_manifest_raw_yaml( + &self, + site: Arc, + raw_yaml: String, + ) -> Result<(), StoreError> { + self.with_conn(move |conn, _| { + deployment::set_manifest_raw_yaml(&conn, &site, &raw_yaml).map_err(Into::into) + }) + .await + } } /// Tries to fetch a [`Table`] either by its Entity name or its SQL name. diff --git a/store/postgres/src/detail.rs b/store/postgres/src/detail.rs index 9f9b65c8da5..90323dbf1d2 100644 --- a/store/postgres/src/detail.rs +++ b/store/postgres/src/detail.rs @@ -340,6 +340,7 @@ struct StoredSubgraphManifest { use_bytea_prefix: bool, start_block_number: Option, start_block_hash: Option, + raw_yaml: Option, } impl From for SubgraphManifestEntity { @@ -350,6 +351,7 @@ impl From for SubgraphManifestEntity { repository: value.repository, features: value.features, schema: value.schema, + raw_yaml: value.raw_yaml, } } } diff --git a/store/postgres/src/dynds/private.rs b/store/postgres/src/dynds/private.rs index 78710db066a..9a4bdf17902 100644 --- a/store/postgres/src/dynds/private.rs +++ b/store/postgres/src/dynds/private.rs @@ -8,6 +8,7 @@ use diesel::{ }; use graph::{ + anyhow::Context, components::store::StoredDynamicDataSource, constraint_violation, prelude::{serde_json, BlockNumber, StoreError}, @@ -206,6 +207,8 @@ impl DataSourcesTable { conn: &PgConnection, dst: &DataSourcesTable, target_block: BlockNumber, + src_manifest_idx_and_name: &[(i32, String)], + dst_manifest_idx_and_name: &[(i32, String)], ) -> Result { // Check if there are any data sources for dst which indicates we already copied let count = dst.table.clone().count().get_result::(conn)?; @@ -213,30 +216,71 @@ impl DataSourcesTable { return Ok(count as usize); } - let query = format!( - "\ - insert into {dst}(block_range, causality_region, manifest_idx, parent, id, param, context) - select case - when upper(e.block_range) <= $1 then e.block_range - else int4range(lower(e.block_range), null) + type Tuple = ( + (Bound, Bound), + i32, + Option>, + Option, + i32, + ); + + let src_tuples = self + .table + .clone() + .filter(diesel::dsl::sql("lower(block_range) <= ").bind::(target_block)) + .select(( + &self.block_range, + &self.manifest_idx, + &self.param, + &self.context, + &self.causality_region, + )) + .order_by(&self.vid) + .load::(conn)?; + + let mut count = 0; + for (block_range, src_manifest_idx, param, context, causality_region) in src_tuples { + let name = &src_manifest_idx_and_name + .iter() + .find(|(idx, _)| idx == &src_manifest_idx) + .context("manifest_idx not found in src")? + .1; + let dst_manifest_idx = dst_manifest_idx_and_name + .iter() + .find(|(_, n)| n == name) + .context("name not found in dst")? + .0; + + let query = format!( + "\ + insert into {dst}(block_range, manifest_idx, param, context, causality_region) + values(case + when upper($2) <= $1 then $2 + else int4range(lower($2), null) end, - e.causality_region, e.manifest_idx, e.parent, e.id, e.param, e.context - from {src} e - where lower(e.block_range) <= $1 + $3, $4, $5, $6) ", - src = self.qname, - dst = dst.qname - ); + dst = dst.qname + ); - let count = sql_query(&query) - .bind::(target_block) - .execute(conn)?; + count += sql_query(&query) + .bind::(target_block) + .bind::, _>(block_range) + .bind::(dst_manifest_idx) + .bind::, _>(param) + .bind::, _>(context) + .bind::(causality_region) + .execute(conn)?; + } - // Test that both tables have the same contents. - debug_assert!( - self.load(conn, target_block).map_err(|e| e.to_string()) - == dst.load(conn, target_block).map_err(|e| e.to_string()) - ); + // If the manifest idxes remained constant, we can test that both tables have the same + // contents. + if src_manifest_idx_and_name == dst_manifest_idx_and_name { + debug_assert!( + self.load(conn, target_block).map_err(|e| e.to_string()) + == dst.load(conn, target_block).map_err(|e| e.to_string()) + ); + } Ok(count) } diff --git a/store/postgres/src/subgraph_store.rs b/store/postgres/src/subgraph_store.rs index 9e1c72040dd..f771dbd7e1c 100644 --- a/store/postgres/src/subgraph_store.rs +++ b/store/postgres/src/subgraph_store.rs @@ -27,8 +27,9 @@ use graph::{ prelude::{ anyhow, futures03::future::join_all, lazy_static, o, web3::types::Address, ApiSchema, ApiVersion, BlockHash, BlockNumber, BlockPtr, ChainStore, DeploymentHash, EntityOperation, - Logger, MetricsRegistry, NodeId, PartialBlockPtr, Schema, StoreError, SubgraphName, - SubgraphStore as SubgraphStoreTrait, SubgraphVersionSwitchingMode, + Logger, MetricsRegistry, NodeId, PartialBlockPtr, Schema, StoreError, + SubgraphDeploymentEntity, SubgraphName, SubgraphStore as SubgraphStoreTrait, + SubgraphVersionSwitchingMode, }, url::Url, util::timed_cache::TimedCache, @@ -1092,6 +1093,11 @@ impl SubgraphStoreInner { .prune(reporter, site, earliest_block, reorg_threshold, prune_ratio) .await } + + pub fn load_deployment(&self, site: &Site) -> Result { + let src_store = self.for_site(site)?; + src_store.load_deployment(site) + } } struct EnsLookup { @@ -1286,4 +1292,13 @@ impl SubgraphStoreTrait for SubgraphStore { .map(|site| site.into()) .collect()) } + + async fn set_manifest_raw_yaml( + &self, + hash: &DeploymentHash, + raw_yaml: String, + ) -> Result<(), StoreError> { + let (store, site) = self.store(hash)?; + store.set_manifest_raw_yaml(site, raw_yaml).await + } } diff --git a/store/postgres/tests/graft.rs b/store/postgres/tests/graft.rs index 1ed2b259c7d..399d17ceb3c 100644 --- a/store/postgres/tests/graft.rs +++ b/store/postgres/tests/graft.rs @@ -144,7 +144,10 @@ async fn insert_test_data(store: Arc) -> DeploymentLocator }; // Create SubgraphDeploymentEntity - let deployment = DeploymentCreate::new(&manifest, None); + let mut yaml = serde_yaml::Mapping::new(); + yaml.insert("dataSources".into(), Vec::::new().into()); + let yaml = serde_yaml::to_string(&yaml).unwrap(); + let deployment = DeploymentCreate::new(yaml, &manifest, None); let name = SubgraphName::new("test/graft").unwrap(); let node_id = NodeId::new("test").unwrap(); let deployment = store diff --git a/store/postgres/tests/store.rs b/store/postgres/tests/store.rs index 156c826ac90..b8b2c9480d1 100644 --- a/store/postgres/tests/store.rs +++ b/store/postgres/tests/store.rs @@ -170,7 +170,7 @@ async fn insert_test_data(store: Arc) -> DeploymentLocator }; // Create SubgraphDeploymentEntity - let deployment = DeploymentCreate::new(&manifest, None); + let deployment = DeploymentCreate::new(String::new(), &manifest, None); let name = SubgraphName::new("test/store").unwrap(); let node_id = NodeId::new("test").unwrap(); let deployment = store @@ -1296,7 +1296,8 @@ fn entity_changes_are_fired_and_forwarded_to_subscriptions() { chain: PhantomData, }; - let deployment = DeploymentCreate::new(&manifest, Some(TEST_BLOCK_0_PTR.clone())); + let deployment = + DeploymentCreate::new(String::new(), &manifest, Some(TEST_BLOCK_0_PTR.clone())); let name = SubgraphName::new("test/entity-changes-are-fired").unwrap(); let node_id = NodeId::new("test").unwrap(); let deployment = store diff --git a/store/postgres/tests/subgraph.rs b/store/postgres/tests/subgraph.rs index 12d6e2c9be7..2c2c4012dd1 100644 --- a/store/postgres/tests/subgraph.rs +++ b/store/postgres/tests/subgraph.rs @@ -148,7 +148,7 @@ fn create_subgraph() { templates: vec![], chain: PhantomData, }; - let deployment = DeploymentCreate::new(&manifest, None); + let deployment = DeploymentCreate::new(String::new(), &manifest, None); let node_id = NodeId::new("left").unwrap(); let (deployment, events) = tap_store_events(|| { diff --git a/store/postgres/tests/writable.rs b/store/postgres/tests/writable.rs index 25e9711facc..85558ebd229 100644 --- a/store/postgres/tests/writable.rs +++ b/store/postgres/tests/writable.rs @@ -47,7 +47,7 @@ async fn insert_test_data(store: Arc) -> DeploymentLocator }; // Create SubgraphDeploymentEntity - let deployment = DeploymentCreate::new(&manifest, None); + let deployment = DeploymentCreate::new(String::new(), &manifest, None); let name = SubgraphName::new("test/writable").unwrap(); let node_id = NodeId::new("test").unwrap(); let deployment = store diff --git a/store/test-store/src/store.rs b/store/test-store/src/store.rs index c1158da0b2a..3e68f20f9f5 100644 --- a/store/test-store/src/store.rs +++ b/store/test-store/src/store.rs @@ -167,7 +167,10 @@ pub async fn create_subgraph( chain: PhantomData, }; - let deployment = DeploymentCreate::new(&manifest, None).graft(base); + let mut yaml = serde_yaml::Mapping::new(); + yaml.insert("dataSources".into(), Vec::::new().into()); + let yaml = serde_yaml::to_string(&yaml).unwrap(); + let deployment = DeploymentCreate::new(yaml, &manifest, None).graft(base); let name = { let mut name = subgraph_id.to_string(); name.truncate(32); diff --git a/tests/integration-tests/data-source-revert/grafted.yaml b/tests/integration-tests/data-source-revert/grafted.yaml index 64e9180a1fc..0d68cdb53ce 100644 --- a/tests/integration-tests/data-source-revert/grafted.yaml +++ b/tests/integration-tests/data-source-revert/grafted.yaml @@ -26,6 +26,26 @@ dataSources: blockHandlers: - handler: handleBlock file: ./src/mapping.ts + # Tests that adding a data source is possible in a graft + - kind: ethereum/contract + name: Contract2 + network: test + source: + address: "0xCfEB869F69431e42cdB54A4F4f105C19C080A601" + abi: Contract + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Gravatar + abis: + - name: Contract + file: ./abis/Contract.abi + callHandlers: + - handler: handleBlock + function: emitTrigger(uint16) + file: ./src/mapping.ts templates: - kind: ethereum/contract name: Template diff --git a/tests/integration-tests/typename/subgraph.yaml b/tests/integration-tests/typename/subgraph.yaml index cc95116e077..1c6ff2eb186 100644 --- a/tests/integration-tests/typename/subgraph.yaml +++ b/tests/integration-tests/typename/subgraph.yaml @@ -13,7 +13,7 @@ dataSources: abi: Contract mapping: kind: ethereum/events - apiVersion: 0.0.5 + apiVersion: 0.0.6 language: wasm/assemblyscript entities: - ExampleEntity From 0d494c224dab9f65786d7bc380a66bd973efd314 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 18:50:19 +0100 Subject: [PATCH 045/253] build(deps): bump semver from 1.0.12 to 1.0.14 (#3994) Bumps [semver](https://github.com/dtolnay/semver) from 1.0.12 to 1.0.14. - [Release notes](https://github.com/dtolnay/semver/releases) - [Commits](https://github.com/dtolnay/semver/compare/1.0.12...1.0.14) --- updated-dependencies: - dependency-name: semver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- chain/cosmos/Cargo.toml | 2 +- chain/ethereum/Cargo.toml | 2 +- chain/substreams/Cargo.toml | 2 +- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13030c7cdf2..6f8d5117ecf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3893,9 +3893,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" dependencies = [ "serde", ] diff --git a/chain/cosmos/Cargo.toml b/chain/cosmos/Cargo.toml index b08c307b783..9bf0d64cca9 100644 --- a/chain/cosmos/Cargo.toml +++ b/chain/cosmos/Cargo.toml @@ -13,7 +13,7 @@ prost = "0.10.1" prost-types = "0.10.1" serde = "1.0" anyhow = "1.0" -semver = "1.0.3" +semver = "1.0.14" graph-runtime-wasm = { path = "../../runtime/wasm" } graph-runtime-derive = { path = "../../runtime/derive" } diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index f322e1cb227..ea36e2d034d 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -17,7 +17,7 @@ dirs-next = "2.0" anyhow = "1.0" tiny-keccak = "1.5.0" hex = "0.4.3" -semver = "1.0.12" +semver = "1.0.14" itertools = "0.10.3" diff --git a/chain/substreams/Cargo.toml b/chain/substreams/Cargo.toml index e1b494e2d76..0f265d5fad5 100644 --- a/chain/substreams/Cargo.toml +++ b/chain/substreams/Cargo.toml @@ -22,7 +22,7 @@ dirs-next = "2.0" anyhow = "1.0" tiny-keccak = "1.5.0" hex = "0.4.3" -semver = "1.0.12" +semver = "1.0.14" itertools = "0.10.3" diff --git a/core/Cargo.toml b/core/Cargo.toml index 44aa8965e90..99c9f876e6d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,7 +20,7 @@ graph-chain-cosmos = { path = "../chain/cosmos" } graph-chain-substreams = { path = "../chain/substreams" } lazy_static = "1.2.0" lru_time_cache = "0.11" -semver = "1.0.12" +semver = "1.0.14" serde = "1.0" serde_json = "1.0" serde_yaml = "0.8" diff --git a/graph/Cargo.toml b/graph/Cargo.toml index e4604daa0af..f3805c79a1c 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -28,7 +28,7 @@ num-bigint = { version = "^0.2.6", features = ["serde"] } num_cpus = "1.13.1" num-traits = "0.2.15" rand = "0.8.4" -semver = { version = "1.0.12", features = ["serde"] } +semver = { version = "1.0.14", features = ["serde"] } serde = { version = "1.0.126", features = ["rc"] } serde_derive = "1.0.125" serde_json = { version = "1.0", features = ["arbitrary_precision"] } diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index 8c5cc02481a..47b2a896726 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -12,7 +12,7 @@ hex = "0.4.3" graph = { path = "../../graph" } bs58 = "0.4.0" graph-runtime-derive = { path = "../derive" } -semver = "1.0.12" +semver = "1.0.14" lazy_static = "1.4" uuid = { version = "1.1.2", features = ["v4"] } strum = "0.21.0" From de79e006daa60adad41a4a7c51c509f1956f436e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 18:50:50 +0100 Subject: [PATCH 046/253] build(deps): bump itertools from 0.10.3 to 0.10.5 (#3996) Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.3 to 0.10.5. - [Release notes](https://github.com/rust-itertools/itertools/releases) - [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-itertools/itertools/commits) --- updated-dependencies: - dependency-name: itertools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- chain/ethereum/Cargo.toml | 2 +- chain/substreams/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- store/postgres/Cargo.toml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f8d5117ecf..48b94a0808f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2420,9 +2420,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index ea36e2d034d..d23de1da1f5 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -19,7 +19,7 @@ tiny-keccak = "1.5.0" hex = "0.4.3" semver = "1.0.14" -itertools = "0.10.3" +itertools = "0.10.5" graph-runtime-wasm = { path = "../../runtime/wasm" } graph-runtime-derive = { path = "../../runtime/derive" } diff --git a/chain/substreams/Cargo.toml b/chain/substreams/Cargo.toml index 0f265d5fad5..f3e74bb09c6 100644 --- a/chain/substreams/Cargo.toml +++ b/chain/substreams/Cargo.toml @@ -24,7 +24,7 @@ tiny-keccak = "1.5.0" hex = "0.4.3" semver = "1.0.14" -itertools = "0.10.3" +itertools = "0.10.5" [dev-dependencies] graph-core = { path = "../../core" } diff --git a/graph/Cargo.toml b/graph/Cargo.toml index f3805c79a1c..5276e90f577 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -57,7 +57,7 @@ futures03 = { version = "0.3.1", package = "futures", features = ["compat"] } wasmparser = "0.78.2" thiserror = "1.0.25" parking_lot = "0.12.1" -itertools = "0.10.3" +itertools = "0.10.5" # Our fork contains patches to make some fields optional for Celo and Fantom compatibility. # Without the "arbitrary_precision" feature, we get the error `data did not match any variant of untagged enum Response`. diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index 4c981b8776e..fad076bc52f 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -30,7 +30,7 @@ stable-hash_legacy = { version = "0.3.3", package = "stable-hash" } diesel_derives = "1.4.1" anyhow = "1.0.65" git-testament = "0.2.0" -itertools = "0.10.3" +itertools = "0.10.5" pin-utils = "0.1" hex = "0.4.3" From e49e4a4ed70d039280cc8fff051307372df0c6eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 18:51:39 +0100 Subject: [PATCH 047/253] build(deps): bump protobuf-parse from 3.1.0 to 3.2.0 (#3997) Bumps [protobuf-parse](https://github.com/stepancheg/rust-protobuf) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/stepancheg/rust-protobuf/releases) - [Changelog](https://github.com/stepancheg/rust-protobuf/blob/master/CHANGELOG.md) - [Commits](https://github.com/stepancheg/rust-protobuf/compare/v3.1.0...v3.2.0) --- updated-dependencies: - dependency-name: protobuf-parse dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 16 ++++++++-------- chain/common/Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 48b94a0808f..87a12fab6b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1594,7 +1594,7 @@ version = "0.28.0" dependencies = [ "anyhow", "heck 0.4.0", - "protobuf 3.1.0", + "protobuf 3.2.0", "protobuf-parse", ] @@ -3429,9 +3429,9 @@ checksum = "020f86b07722c5c4291f7c723eac4676b3892d47d9a7708dc2779696407f039b" [[package]] name = "protobuf" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee4a7d8b91800c8f167a6268d1a1026607368e1adc84e98fe044aeb905302f7" +checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" dependencies = [ "once_cell", "protobuf-support", @@ -3440,14 +3440,14 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1447dd751c434cc1b415579837ebd0411ed7d67d465f38010da5d7cd33af4d" +checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" dependencies = [ "anyhow", "indexmap", "log", - "protobuf 3.1.0", + "protobuf 3.2.0", "protobuf-support", "tempfile", "thiserror", @@ -3456,9 +3456,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca157fe12fc7ee2e315f2f735e27df41b3d97cdd70ea112824dac1ffb08ee1c" +checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" dependencies = [ "thiserror", ] diff --git a/chain/common/Cargo.toml b/chain/common/Cargo.toml index 5c6d2517141..2d92f11dcd4 100644 --- a/chain/common/Cargo.toml +++ b/chain/common/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" [dependencies] protobuf = "3.0.2" -protobuf-parse = "3.0.2" +protobuf-parse = "3.2.0" anyhow = "1" heck = "0.4" From d74449cadec9ff14f641a028ad18502227e85fdb Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Mon, 3 Oct 2022 18:30:24 +0000 Subject: [PATCH 048/253] disable forced static filters (#4021) --- graph/src/env/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/graph/src/env/mod.rs b/graph/src/env/mod.rs index ba21c848c6f..83de6d8e4ce 100644 --- a/graph/src/env/mod.rs +++ b/graph/src/env/mod.rs @@ -375,7 +375,8 @@ struct Inner { external_http_base_url: Option, #[envconfig(from = "EXTERNAL_WS_BASE_URL")] external_ws_base_url: Option, - #[envconfig(from = "GRAPH_STATIC_FILTERS_THRESHOLD", default = "10000")] + // Setting this to be unrealistically high so it doesn't get triggered. + #[envconfig(from = "GRAPH_STATIC_FILTERS_THRESHOLD", default = "100000000")] static_filters_threshold: usize, } From e12ef3d1fba372306cdfb3438e4b5c51304d8ed7 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Tue, 4 Oct 2022 11:03:49 +0100 Subject: [PATCH 049/253] fix: Ensure retry backoff base is less than ceil (#4019) --- core/src/subgraph/runner.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/subgraph/runner.rs b/core/src/subgraph/runner.rs index 9ae886ce805..73c03e1803c 100644 --- a/core/src/subgraph/runner.rs +++ b/core/src/subgraph/runner.rs @@ -57,7 +57,10 @@ where should_try_unfail_non_deterministic: true, synced: false, skip_ptr_updates_timer: Instant::now(), - backoff: ExponentialBackoff::new(MINUTE * 2, ENV_VARS.subgraph_error_retry_ceil), + backoff: ExponentialBackoff::new( + (MINUTE * 2).min(ENV_VARS.subgraph_error_retry_ceil), + ENV_VARS.subgraph_error_retry_ceil, + ), entity_lfu_cache: LfuCache::new(), }, logger, From 0c4417e50da3ea40d492ee2373bbfdf82ec09f0f Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Tue, 4 Oct 2022 12:13:58 +0100 Subject: [PATCH 050/253] fix(store): Check if graft is done (#4027) * fix(store): Check if graft is done * refactor: Add context to `load_deployment` error --- store/postgres/src/deployment_store.rs | 160 +++++++++++++------------ 1 file changed, 84 insertions(+), 76 deletions(-) diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index 34f6669fca5..669d0ebca3b 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -3,6 +3,7 @@ use diesel::connection::SimpleConnection; use diesel::pg::PgConnection; use diesel::prelude::*; use diesel::r2d2::{ConnectionManager, PooledConnection}; +use graph::anyhow::Context; use graph::blockchain::block_stream::FirehoseCursor; use graph::components::store::{EntityKey, EntityType, PruneReporter, StoredDynamicDataSource}; use graph::components::versions::VERSIONS; @@ -211,7 +212,8 @@ impl DeploymentStore { site: &Site, ) -> Result { let conn = self.get_conn()?; - detail::deployment_entity(&conn, site) + Ok(detail::deployment_entity(&conn, site) + .with_context(|| format!("Deployment details not found for {}", site.deployment))?) } // Remove the data and metadata for the deployment `site`. This operation @@ -1309,92 +1311,98 @@ impl DeploymentStore { // Do any cleanup to bring the subgraph into a known good state if let Some((src, block)) = graft_src { - info!( - logger, - "Initializing graft by copying data from {} to {}", - src.catalog.site.namespace, - dst.catalog.site.namespace - ); - - let src_manifest_idx_and_name = self - .load_deployment(&src.site)? - .manifest - .template_idx_and_name()?; - let dst_manifest_idx_and_name = self - .load_deployment(&dst.site)? - .manifest - .template_idx_and_name()?; - - // Copy subgraph data - // We allow both not copying tables at all from the source, as well - // as adding new tables in `self`; we only need to check that tables - // that actually need to be copied from the source are compatible - // with the corresponding tables in `self` - let copy_conn = crate::copy::Connection::new( - logger, - self.pool.clone(), - src.clone(), - dst.clone(), - block.clone(), - src_manifest_idx_and_name, - dst_manifest_idx_and_name, - )?; - let status = copy_conn.copy_data()?; - if status == crate::copy::Status::Cancelled { - return Err(StoreError::Canceled); - } - let conn = self.get_conn()?; - conn.transaction(|| -> Result<(), StoreError> { - // Copy shared dynamic data sources and adjust their ID; if - // the subgraph uses private data sources, that is done by - // `copy::Connection::copy_data` since it requires access to - // the source schema which in sharded setups is only - // available while that function runs - let start = Instant::now(); - let count = dynds::shared::copy(&conn, &src.site, &dst.site, block.number)?; - info!(logger, "Copied {} dynamic data sources", count; + let dst_block_ptr = Self::block_ptr_with_conn(&conn, dst.site.cheap_clone())?; + + // If the dst block is past the graft point, then the graft has already been completed. + if dst_block_ptr.map(|ptr| ptr.number) < Some(block.number) { + info!( + logger, + "Initializing graft by copying data from {} to {}", + src.catalog.site.namespace, + dst.catalog.site.namespace + ); + + let src_manifest_idx_and_name = self + .load_deployment(&src.site)? + .manifest + .template_idx_and_name()?; + let dst_manifest_idx_and_name = self + .load_deployment(&dst.site)? + .manifest + .template_idx_and_name()?; + + // Copy subgraph data + // We allow both not copying tables at all from the source, as well + // as adding new tables in `self`; we only need to check that tables + // that actually need to be copied from the source are compatible + // with the corresponding tables in `self` + let copy_conn = crate::copy::Connection::new( + logger, + self.pool.clone(), + src.clone(), + dst.clone(), + block.clone(), + src_manifest_idx_and_name, + dst_manifest_idx_and_name, + )?; + let status = copy_conn.copy_data()?; + if status == crate::copy::Status::Cancelled { + return Err(StoreError::Canceled); + } + + let conn = self.get_conn()?; + conn.transaction(|| -> Result<(), StoreError> { + // Copy shared dynamic data sources and adjust their ID; if + // the subgraph uses private data sources, that is done by + // `copy::Connection::copy_data` since it requires access to + // the source schema which in sharded setups is only + // available while that function runs + let start = Instant::now(); + let count = dynds::shared::copy(&conn, &src.site, &dst.site, block.number)?; + info!(logger, "Copied {} dynamic data sources", count; "time_ms" => start.elapsed().as_millis()); - // Copy errors across - let start = Instant::now(); - let count = deployment::copy_errors(&conn, &src.site, &dst.site, &block)?; - info!(logger, "Copied {} existing errors", count; + // Copy errors across + let start = Instant::now(); + let count = deployment::copy_errors(&conn, &src.site, &dst.site, &block)?; + info!(logger, "Copied {} existing errors", count; "time_ms" => start.elapsed().as_millis()); - catalog::copy_account_like(&conn, &src.site, &dst.site)?; - - // Rewind the subgraph so that entity versions that are - // clamped in the future (beyond `block`) become valid for - // all blocks after `block`. `revert_block` gets rid of - // everything including the block passed to it. We want to - // preserve `block` and therefore revert `block+1` - let start = Instant::now(); - let block_to_revert: BlockNumber = block - .number - .checked_add(1) - .expect("block numbers fit into an i32"); - dst.revert_block(&conn, block_to_revert)?; - info!(logger, "Rewound subgraph to block {}", block.number; + catalog::copy_account_like(&conn, &src.site, &dst.site)?; + + // Rewind the subgraph so that entity versions that are + // clamped in the future (beyond `block`) become valid for + // all blocks after `block`. `revert_block` gets rid of + // everything including the block passed to it. We want to + // preserve `block` and therefore revert `block+1` + let start = Instant::now(); + let block_to_revert: BlockNumber = block + .number + .checked_add(1) + .expect("block numbers fit into an i32"); + dst.revert_block(&conn, block_to_revert)?; + info!(logger, "Rewound subgraph to block {}", block.number; "time_ms" => start.elapsed().as_millis()); - let start = Instant::now(); - deployment::set_entity_count(&conn, &dst.site, &dst.count_query)?; - info!(logger, "Counted the entities"; + let start = Instant::now(); + deployment::set_entity_count(&conn, &dst.site, &dst.count_query)?; + info!(logger, "Counted the entities"; "time_ms" => start.elapsed().as_millis()); - // Analyze all tables for this deployment - for entity_name in dst.tables.keys() { - self.analyze_with_conn(site.cheap_clone(), entity_name.as_str(), &conn)?; - } + // Analyze all tables for this deployment + for entity_name in dst.tables.keys() { + self.analyze_with_conn(site.cheap_clone(), entity_name.as_str(), &conn)?; + } - // Set the block ptr to the graft point to signal that we successfully - // performed the graft - crate::deployment::forward_block_ptr(&conn, &dst.site.deployment, &block)?; - info!(logger, "Subgraph successfully initialized"; + // Set the block ptr to the graft point to signal that we successfully + // performed the graft + crate::deployment::forward_block_ptr(&conn, &dst.site.deployment, &block)?; + info!(logger, "Subgraph successfully initialized"; "time_ms" => start.elapsed().as_millis()); - Ok(()) - })?; + Ok(()) + })?; + } } // Make sure the block pointer is set. This is important for newly // deployed subgraphs so that we respect the 'startBlock' setting From eed21c73f62b77490c3a66199d49735addd1e47c Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Tue, 4 Oct 2022 13:19:54 +0100 Subject: [PATCH 051/253] fix: Check if subgraph is deployed before set_manifest_raw_yaml (#4028) --- core/src/subgraph/instance_manager.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/core/src/subgraph/instance_manager.rs b/core/src/subgraph/instance_manager.rs index 5a575bdd27c..0f05f5359d1 100644 --- a/core/src/subgraph/instance_manager.rs +++ b/core/src/subgraph/instance_manager.rs @@ -184,14 +184,17 @@ impl SubgraphInstanceManager { .set_manifest_raw_yaml(&deployment.hash, raw_yaml) .await?; if let Some(graft) = &manifest.graft { - let file_bytes = self - .link_resolver - .cat(&logger, &graft.base.to_ipfs_link()) - .await?; - let yaml = String::from_utf8(file_bytes)?; - self.subgraph_store - .set_manifest_raw_yaml(&graft.base, yaml) - .await?; + if self.subgraph_store.is_deployed(&graft.base)? { + let file_bytes = self + .link_resolver + .cat(&logger, &graft.base.to_ipfs_link()) + .await?; + let yaml = String::from_utf8(file_bytes)?; + + self.subgraph_store + .set_manifest_raw_yaml(&graft.base, yaml) + .await?; + } } info!(logger, "Resolve subgraph files using IPFS"); From d52c5e63b6d160c06ebc280397b77065a9c3eebe Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 23 Sep 2022 16:01:05 -0700 Subject: [PATCH 052/253] store: Accept &str in catalog::table_exists as the namespace --- store/postgres/src/catalog.rs | 4 ++-- store/postgres/src/relational/prune.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 4a0a8d043c6..bb67ef00d88 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -161,7 +161,7 @@ fn get_text_columns( pub fn table_exists( conn: &PgConnection, - namespace: &Namespace, + namespace: &str, table: &SqlName, ) -> Result { #[derive(Debug, QueryableByName)] @@ -186,7 +186,7 @@ pub fn supports_proof_of_indexing( lazy_static! { static ref POI_TABLE_NAME: SqlName = SqlName::verbatim(POI_TABLE.to_owned()); } - table_exists(conn, namespace, &POI_TABLE_NAME) + table_exists(conn, namespace.as_str(), &POI_TABLE_NAME) } /// Whether the given table has an exclusion constraint. When we create diff --git a/store/postgres/src/relational/prune.rs b/store/postgres/src/relational/prune.rs index fcc04b76567..ed7d83a63cf 100644 --- a/store/postgres/src/relational/prune.rs +++ b/store/postgres/src/relational/prune.rs @@ -41,7 +41,7 @@ impl TablePair { let dst = src.new_like(&layout.site.namespace, &new_name); let mut query = String::new(); - if catalog::table_exists(conn, &layout.site.namespace, &dst.name)? { + if catalog::table_exists(conn, layout.site.namespace.as_str(), &dst.name)? { writeln!(query, "truncate table {nsp}.{new_name};")?; } else { dst.create_table(&mut query, layout)?; From f93b0d2d0688efb27c2955af91f3205961646d98 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Mon, 19 Sep 2022 09:50:50 -0700 Subject: [PATCH 053/253] store: Detect if there were any migrations to actually run --- store/postgres/src/catalog.rs | 25 ++++++++++++++++++++++++- store/postgres/src/connection_pool.rs | 21 +++++++++++---------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index bb67ef00d88..3bf19c8d0b5 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -21,7 +21,7 @@ use graph::{ use crate::connection_pool::ForeignServer; use crate::{ - primary::{Namespace, Site}, + primary::{Namespace, Site, NAMESPACE_PUBLIC}, relational::SqlName, }; @@ -52,6 +52,19 @@ table! { } } +table! { + __diesel_schema_migrations(version) { + version -> Text, + run_on -> Timestamp, + } +} + +lazy_static! { + /// The name of the table in which Diesel records migrations + static ref MIGRATIONS_TABLE: SqlName = + SqlName::verbatim("__diesel_schema_migrations".to_string()); +} + // In debug builds (for testing etc.) create exclusion constraints, in // release builds for production, skip them #[cfg(debug_assertions)] @@ -310,6 +323,16 @@ pub fn recreate_schema(conn: &PgConnection, nsp: &str) -> Result<(), StoreError> Ok(conn.batch_execute(&query)?) } +pub fn migration_count(conn: &PgConnection) -> Result { + use __diesel_schema_migrations as m; + + if !table_exists(conn, NAMESPACE_PUBLIC, &*MIGRATIONS_TABLE)? { + return Ok(0); + } + + m::table.count().get_result(conn).map_err(StoreError::from) +} + pub fn account_like(conn: &PgConnection, site: &Site) -> Result, StoreError> { use table_stats as ts; let names = ts::table diff --git a/store/postgres/src/connection_pool.rs b/store/postgres/src/connection_pool.rs index 3e665a1abd9..45ec255c1c6 100644 --- a/store/postgres/src/connection_pool.rs +++ b/store/postgres/src/connection_pool.rs @@ -983,7 +983,7 @@ impl PoolInner { let result = pool .configure_fdw(servers.as_ref()) .and_then(|()| migrate_schema(&pool.logger, &conn)) - .and_then(|()| pool.map_primary()) + .and_then(|had_migrations| pool.map_primary()) .and_then(|()| pool.map_metadata(servers.as_ref())); debug!(&pool.logger, "Release migration lock"); advisory_lock::unlock_migration(&conn).unwrap_or_else(|err| { @@ -1068,19 +1068,22 @@ embed_migrations!("./migrations"); /// When multiple `graph-node` processes start up at the same time, we ensure /// that they do not run migrations in parallel by using `blocking_conn` to /// serialize them. The `conn` is used to run the actual migration. -fn migrate_schema(logger: &Logger, conn: &PgConnection) -> Result<(), StoreError> { +fn migrate_schema(logger: &Logger, conn: &PgConnection) -> Result { // Collect migration logging output let mut output = vec![]; + let old_count = catalog::migration_count(conn)?; + info!(logger, "Running migrations"); let result = embedded_migrations::run_with_output(conn, &mut output); info!(logger, "Migrations finished"); + let had_migrations = catalog::migration_count(conn)? != old_count; + // If there was any migration output, log it now let msg = String::from_utf8(output).unwrap_or_else(|_| String::from("")); let msg = msg.trim(); - let has_output = !msg.is_empty(); - if has_output { + if !msg.is_empty() { let msg = msg.replace('\n', " "); if let Err(e) = result { error!(logger, "Postgres migration error"; "output" => msg); @@ -1090,13 +1093,11 @@ fn migrate_schema(logger: &Logger, conn: &PgConnection) -> Result<(), StoreError } } - if has_output { - // We take getting output as a signal that a migration was actually - // run, which is not easy to tell from the Diesel API, and reset the - // query statistics since a schema change makes them not all that - // useful. An error here is not serious and can be ignored. + if had_migrations { + // Reset the query statistics since a schema change makes them not + // all that useful. An error here is not serious and can be ignored. conn.batch_execute("select pg_stat_statements_reset()").ok(); } - Ok(()) + Ok(had_migrations) } From 9ffc739c9eba5b427d8cc177d77d23415c3ec8f7 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 23 Sep 2022 15:33:09 -0700 Subject: [PATCH 054/253] node, store: Properly propagate schema changes in a shard --- node/src/bin/manager.rs | 4 +- node/src/store_builder.rs | 24 +++-- store/postgres/src/connection_pool.rs | 146 ++++++++++++++++++++------ 3 files changed, 127 insertions(+), 47 deletions(-) diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index f68a2dbdfed..7715c8c5c1d 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -21,6 +21,7 @@ use graph_node::{ store_builder::StoreBuilder, MetricsContext, }; +use graph_store_postgres::connection_pool::PoolCoordinator; use graph_store_postgres::ChainStore; use graph_store_postgres::{ connection_pool::ConnectionPool, BlockStore, NotificationSender, Shard, Store, SubgraphStore, @@ -587,13 +588,14 @@ impl Context { fn primary_pool(self) -> ConnectionPool { let primary = self.config.primary_store(); + let coord = Arc::new(PoolCoordinator::new(Arc::new(vec![]))); let pool = StoreBuilder::main_pool( &self.logger, &self.node_id, PRIMARY_SHARD.as_str(), primary, self.metrics_registry(), - Arc::new(vec![]), + coord, ); pool.skip_setup(); pool diff --git a/node/src/store_builder.rs b/node/src/store_builder.rs index 205f182d06f..786c1ff6e73 100644 --- a/node/src/store_builder.rs +++ b/node/src/store_builder.rs @@ -9,7 +9,9 @@ use graph::{ prelude::{info, CheapClone, Logger}, util::security::SafeDisplay, }; -use graph_store_postgres::connection_pool::{ConnectionPool, ForeignServer, PoolName}; +use graph_store_postgres::connection_pool::{ + ConnectionPool, ForeignServer, PoolCoordinator, PoolName, +}; use graph_store_postgres::{ BlockStore as DieselBlockStore, ChainHeadUpdateListener as PostgresChainHeadUpdateListener, NotificationSender, Shard as ShardName, Store as DieselStore, SubgraphStore, @@ -102,6 +104,7 @@ impl StoreBuilder { .collect::, _>>() .expect("connection url's contain enough detail"); let servers = Arc::new(servers); + let coord = Arc::new(PoolCoordinator::new(servers)); let shards: Vec<_> = config .stores @@ -114,7 +117,7 @@ impl StoreBuilder { name, shard, registry.cheap_clone(), - servers.clone(), + coord.clone(), ); let (read_only_conn_pools, weights) = Self::replica_pools( @@ -123,7 +126,7 @@ impl StoreBuilder { name, shard, registry.cheap_clone(), - servers.clone(), + coord.clone(), ); let name = @@ -191,7 +194,7 @@ impl StoreBuilder { name: &str, shard: &Shard, registry: Arc, - servers: Arc>, + coord: Arc, ) -> ConnectionPool { let logger = logger.new(o!("pool" => "main")); let pool_size = shard.pool_size.size_for(node, name).expect(&format!( @@ -209,15 +212,14 @@ impl StoreBuilder { "conn_pool_size" => pool_size, "weight" => shard.weight ); - ConnectionPool::create( + coord.create_pool( + &logger, name, PoolName::Main, shard.connection.to_owned(), pool_size, Some(fdw_pool_size), - &logger, registry.cheap_clone(), - servers, ) } @@ -228,7 +230,7 @@ impl StoreBuilder { name: &str, shard: &Shard, registry: Arc, - servers: Arc>, + coord: Arc, ) -> (Vec, Vec) { let mut weights: Vec<_> = vec![shard.weight]; ( @@ -250,15 +252,15 @@ impl StoreBuilder { "we can determine the pool size for replica {}", name )); - ConnectionPool::create( + + coord.clone().create_pool( + &logger, name, PoolName::Replica(pool), replica.connection.clone(), pool_size, None, - &logger, registry.cheap_clone(), - servers.clone(), ) }) .collect(), diff --git a/store/postgres/src/connection_pool.rs b/store/postgres/src/connection_pool.rs index 45ec255c1c6..5137e408c02 100644 --- a/store/postgres/src/connection_pool.rs +++ b/store/postgres/src/connection_pool.rs @@ -25,7 +25,7 @@ use graph::{ use std::fmt::{self, Write}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; use std::{collections::HashMap, sync::RwLock}; @@ -158,9 +158,6 @@ impl ForeignServer { /// Map key tables from the primary into our local schema. If we are the /// primary, set them up as views. - /// - /// We recreate this mapping on every server start so that migrations that - /// change one of the mapped tables actually show up in the imported tables fn map_primary(conn: &PgConnection, shard: &Shard) -> Result<(), StoreError> { catalog::recreate_schema(conn, Self::PRIMARY_PUBLIC)?; @@ -226,7 +223,7 @@ const FDW_IDLE_TIMEOUT: Duration = Duration::from_secs(60); enum PoolState { /// A connection pool, and all the servers for which we need to /// establish fdw mappings when we call `setup` on the pool - Created(Arc, Arc>), + Created(Arc, Arc), /// The pool has been successfully set up Ready(Arc), /// The pool has been disabled by setting its size to 0 @@ -300,7 +297,7 @@ impl PoolStateTracker { } impl ConnectionPool { - pub fn create( + fn create( shard_name: &str, pool_name: PoolName, postgres_url: String, @@ -308,7 +305,7 @@ impl ConnectionPool { fdw_pool_size: Option, logger: &Logger, registry: Arc, - servers: Arc>, + coord: Arc, ) -> ConnectionPool { let state_tracker = PoolStateTracker::new(); let shard = @@ -330,7 +327,7 @@ impl ConnectionPool { if pool_name.is_replica() { PoolState::Ready(Arc::new(pool)) } else { - PoolState::Created(Arc::new(pool), servers) + PoolState::Created(Arc::new(pool), coord) } } }; @@ -968,7 +965,7 @@ impl PoolInner { /// # Panics /// /// If any errors happen during the migration, the process panics - pub fn setup(&self, servers: Arc>) -> Result<(), StoreError> { + fn setup(&self, coord: Arc) -> Result<(), StoreError> { fn die(logger: &Logger, msg: &'static str, err: &dyn std::fmt::Display) -> ! { crit!(logger, "{}", msg; "error" => format!("{:#}", err)); panic!("{}: {}", msg, err); @@ -980,11 +977,29 @@ impl PoolInner { let start = Instant::now(); advisory_lock::lock_migration(&conn) .unwrap_or_else(|err| die(&pool.logger, "failed to get migration lock", &err)); + // This code can cause a race in database setup: if pool A has had + // schema changes and pool B then tries to map tables from pool A, + // but does so before the concurrent thread running this code for + // pool B has at least finished `configure_fdw`, mapping tables will + // fail. In that case, the node must be restarted. The restart is + // guaranteed because this failure will lead to a panic in the setup + // for pool A + // + // This code can also leave the table mappings in a state where they + // have not been updated if the process is killed after migrating + // the schema but before finishing remapping in all shards. + // Addressing that would require keeping track of the need to remap + // in the database instead of just in memory let result = pool - .configure_fdw(servers.as_ref()) + .configure_fdw(coord.servers.as_ref()) .and_then(|()| migrate_schema(&pool.logger, &conn)) - .and_then(|had_migrations| pool.map_primary()) - .and_then(|()| pool.map_metadata(servers.as_ref())); + .and_then(|had_migrations| { + if had_migrations { + coord.propagate_schema_change(&self.shard) + } else { + Ok(()) + } + }); debug!(&pool.logger, "Release migration lock"); advisory_lock::unlock_migration(&conn).unwrap_or_else(|err| { die(&pool.logger, "failed to release migration lock", &err); @@ -1021,17 +1036,6 @@ impl PoolInner { }) } - /// Map key tables from the primary into our local schema. If we are the - /// primary, set them up as views. - /// - /// We recreate this mapping on every server start so that migrations that - /// change one of the mapped tables actually show up in the imported tables - fn map_primary(&self) -> Result<(), StoreError> { - info!(&self.logger, "Mapping primary"); - let conn = self.get()?; - conn.transaction(|| ForeignServer::map_primary(&conn, &self.shard)) - } - /// Copy the data from key tables in the primary into our local schema /// so it can be used as a fallback when the primary goes down pub async fn mirror_primary_tables(&self) -> Result<(), StoreError> { @@ -1046,18 +1050,21 @@ impl PoolInner { .await } - // Map some tables from the `subgraphs` metadata schema from foreign - // servers to ourselves. The mapping is recreated on every server start - // so that we pick up possible schema changes in the mappings - fn map_metadata(&self, servers: &[ForeignServer]) -> Result<(), StoreError> { - info!(&self.logger, "Mapping metadata"); - let conn = self.get()?; - conn.transaction(|| { - for server in servers.iter().filter(|server| server.shard != self.shard) { - server.map_metadata(&conn)?; - } - Ok(()) - }) + // The foreign server `server` had schema changes, and we therefore need + // to remap anything that we are importing via fdw to make sure we are + // using this updated schema + fn remap(&self, server: &ForeignServer) -> Result<(), StoreError> { + if &server.shard == &*PRIMARY_SHARD { + info!(&self.logger, "Mapping primary"); + let conn = self.get()?; + conn.transaction(|| ForeignServer::map_primary(&conn, &self.shard))?; + } + if &server.shard != &self.shard { + info!(&self.logger, "Mapping metadata"); + let conn = self.get()?; + conn.transaction(|| server.map_metadata(&conn))?; + } + Ok(()) } } @@ -1101,3 +1108,72 @@ fn migrate_schema(logger: &Logger, conn: &PgConnection) -> Result>>, + servers: Arc>, +} + +impl PoolCoordinator { + pub fn new(servers: Arc>) -> Self { + Self { + pools: Mutex::new(HashMap::new()), + servers, + } + } + + pub fn create_pool( + self: Arc, + logger: &Logger, + name: &str, + pool_name: PoolName, + postgres_url: String, + pool_size: u32, + fdw_pool_size: Option, + registry: Arc, + ) -> ConnectionPool { + let pool = ConnectionPool::create( + name, + pool_name, + postgres_url, + pool_size, + fdw_pool_size, + logger, + registry, + self.cheap_clone(), + ); + // It is safe to take this lock here since nobody has seen the pool + // yet. We remember the `PoolInner` so that later, when we have to + // call `remap()`, we do not have to take this lock as that will be + // already held in `get_ready()` + match &*pool.inner.lock(logger) { + PoolState::Created(inner, _) | PoolState::Ready(inner) => { + self.pools + .lock() + .unwrap() + .insert(pool.shard.clone(), inner.clone()); + } + PoolState::Disabled => { /* nothing to do */ } + } + pool + } + + /// Propagate changes to the schema in `shard` to all other pools. Those + /// other pools will then recreate any tables that they imported from + /// `shard` + fn propagate_schema_change(&self, shard: &Shard) -> Result<(), StoreError> { + let server = self + .servers + .iter() + .find(|server| &server.shard == shard) + .ok_or_else(|| constraint_violation!("unknown shard {shard}"))?; + + for pool in self.pools.lock().unwrap().values() { + pool.remap(server)?; + } + Ok(()) + } +} From ef75f314711efb5715a043efe5b11b7db0cac0c1 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Thu, 29 Sep 2022 14:34:52 -0700 Subject: [PATCH 055/253] node, store: Expose migrating and remapping schemas in graphman --- node/src/bin/manager.rs | 32 ++++++++++++++++++++++++++- node/src/manager/commands/database.rs | 22 ++++++++++++++++++ node/src/manager/commands/mod.rs | 1 + node/src/store_builder.rs | 12 +++++++--- store/postgres/src/connection_pool.rs | 17 ++++++++++++-- 5 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 node/src/manager/commands/database.rs diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index 7715c8c5c1d..786164b0476 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -233,6 +233,10 @@ pub enum Command { #[clap(long, short, default_value = "10000")] history: usize, }, + + /// General database management + #[clap(subcommand)] + Database(DatabaseCommand), } impl Command { @@ -495,6 +499,18 @@ pub enum IndexCommand { }, } +#[derive(Clone, Debug, Subcommand)] +pub enum DatabaseCommand { + /// Apply any pending migrations to the database schema in all shards + Migrate, + /// Refresh the mapping of tables into different shards + /// + /// This command rebuilds the mappings of tables from one shard into all + /// other shards. It makes it possible to fix these mappings when a + /// database migration was interrupted before it could rebuild the + /// mappings + Remap, +} #[derive(Clone, Debug, Subcommand)] pub enum CheckBlockMethod { /// The number of the target block @@ -644,7 +660,7 @@ impl Context { } fn store_and_pools(self) -> (Arc, HashMap) { - let (subgraph_store, pools) = StoreBuilder::make_subgraph_store_and_pools( + let (subgraph_store, pools, _) = StoreBuilder::make_subgraph_store_and_pools( &self.logger, &self.node_id, &self.config, @@ -1059,6 +1075,20 @@ async fn main() -> anyhow::Result<()> { } } } + Database(cmd) => { + match cmd { + DatabaseCommand::Migrate => { + /* creating the store builder runs migrations */ + let _store_builder = ctx.store_builder().await; + println!("All database migrations have been applied"); + Ok(()) + } + DatabaseCommand::Remap => { + let store_builder = ctx.store_builder().await; + commands::database::remap(&store_builder.coord).await + } + } + } Prune { deployment, history, diff --git a/node/src/manager/commands/database.rs b/node/src/manager/commands/database.rs new file mode 100644 index 00000000000..6d335c462b1 --- /dev/null +++ b/node/src/manager/commands/database.rs @@ -0,0 +1,22 @@ +use std::time::Instant; + +use graph::prelude::anyhow; +use graph_store_postgres::connection_pool::PoolCoordinator; + +pub async fn remap(coord: &PoolCoordinator) -> Result<(), anyhow::Error> { + let pools = coord.pools(); + let servers = coord.servers(); + + for server in servers.iter() { + for pool in pools.iter() { + let start = Instant::now(); + print!( + "Remapping imports from {} in shard {}", + server.shard, pool.shard + ); + pool.remap(server)?; + println!(" (done in {}s)", start.elapsed().as_secs()); + } + } + Ok(()) +} diff --git a/node/src/manager/commands/mod.rs b/node/src/manager/commands/mod.rs index eab1f1a0f7a..8c87bf3a68b 100644 --- a/node/src/manager/commands/mod.rs +++ b/node/src/manager/commands/mod.rs @@ -4,6 +4,7 @@ pub mod check_blocks; pub mod config; pub mod copy; pub mod create; +pub mod database; pub mod index; pub mod info; pub mod listen; diff --git a/node/src/store_builder.rs b/node/src/store_builder.rs index 786c1ff6e73..7a7b139c21b 100644 --- a/node/src/store_builder.rs +++ b/node/src/store_builder.rs @@ -28,6 +28,7 @@ pub struct StoreBuilder { chain_head_update_listener: Arc, /// Map network names to the shards where they are/should be stored chains: HashMap, + pub coord: Arc, } impl StoreBuilder { @@ -49,7 +50,7 @@ impl StoreBuilder { registry.clone(), )); - let (store, pools) = Self::make_subgraph_store_and_pools( + let (store, pools, coord) = Self::make_subgraph_store_and_pools( logger, node, config, @@ -82,6 +83,7 @@ impl StoreBuilder { subscription_manager, chain_head_update_listener, chains, + coord, } } @@ -94,7 +96,11 @@ impl StoreBuilder { config: &Config, fork_base: Option, registry: Arc, - ) -> (Arc, HashMap) { + ) -> ( + Arc, + HashMap, + Arc, + ) { let notification_sender = Arc::new(NotificationSender::new(registry.cheap_clone())); let servers = config @@ -150,7 +156,7 @@ impl StoreBuilder { registry, )); - (store, pools) + (store, pools, coord) } pub fn make_store( diff --git a/store/postgres/src/connection_pool.rs b/store/postgres/src/connection_pool.rs index 5137e408c02..130dd5e1ba3 100644 --- a/store/postgres/src/connection_pool.rs +++ b/store/postgres/src/connection_pool.rs @@ -673,7 +673,7 @@ impl HandleEvent for EventHandler { #[derive(Clone)] pub struct PoolInner { logger: Logger, - shard: Shard, + pub shard: Shard, pool: Pool>, // A separate pool for connections that will use foreign data wrappers. // Once such a connection accesses a foreign table, Postgres keeps a @@ -1053,7 +1053,7 @@ impl PoolInner { // The foreign server `server` had schema changes, and we therefore need // to remap anything that we are importing via fdw to make sure we are // using this updated schema - fn remap(&self, server: &ForeignServer) -> Result<(), StoreError> { + pub fn remap(&self, server: &ForeignServer) -> Result<(), StoreError> { if &server.shard == &*PRIMARY_SHARD { info!(&self.logger, "Mapping primary"); let conn = self.get()?; @@ -1176,4 +1176,17 @@ impl PoolCoordinator { } Ok(()) } + + pub fn pools(&self) -> Vec> { + self.pools + .lock() + .unwrap() + .values() + .map(|pool| pool.clone()) + .collect() + } + + pub fn servers(&self) -> Arc> { + self.servers.clone() + } } From 0f08b81baf72795b52c2c223e62ab130bae829f7 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Wed, 5 Oct 2022 00:21:27 +0100 Subject: [PATCH 056/253] Fix cross shard graft (#4030) * fix(store): Partially revert 0c4417e50da3ea40d492ee2373bbfdf82ec09f0f * fix(store): Fix cross shard grafting --- store/postgres/src/deployment_store.rs | 159 ++++++++++++------------- store/postgres/src/writable.rs | 10 +- 2 files changed, 83 insertions(+), 86 deletions(-) diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index 669d0ebca3b..814d4adac04 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -1305,104 +1305,95 @@ impl DeploymentStore { &self, logger: &Logger, site: Arc, - graft_src: Option<(Arc, BlockPtr)>, + graft_src: Option<(Arc, BlockPtr, SubgraphDeploymentEntity)>, ) -> Result<(), StoreError> { let dst = self.find_layout(site.cheap_clone())?; - // Do any cleanup to bring the subgraph into a known good state - if let Some((src, block)) = graft_src { - let conn = self.get_conn()?; - let dst_block_ptr = Self::block_ptr_with_conn(&conn, dst.site.cheap_clone())?; - - // If the dst block is past the graft point, then the graft has already been completed. - if dst_block_ptr.map(|ptr| ptr.number) < Some(block.number) { - info!( - logger, - "Initializing graft by copying data from {} to {}", - src.catalog.site.namespace, - dst.catalog.site.namespace - ); - - let src_manifest_idx_and_name = self - .load_deployment(&src.site)? - .manifest - .template_idx_and_name()?; - let dst_manifest_idx_and_name = self - .load_deployment(&dst.site)? - .manifest - .template_idx_and_name()?; - - // Copy subgraph data - // We allow both not copying tables at all from the source, as well - // as adding new tables in `self`; we only need to check that tables - // that actually need to be copied from the source are compatible - // with the corresponding tables in `self` - let copy_conn = crate::copy::Connection::new( - logger, - self.pool.clone(), - src.clone(), - dst.clone(), - block.clone(), - src_manifest_idx_and_name, - dst_manifest_idx_and_name, - )?; - let status = copy_conn.copy_data()?; - if status == crate::copy::Status::Cancelled { - return Err(StoreError::Canceled); - } + // If `graft_src` is `Some`, then there is a pending graft. + if let Some((src, block, src_deployment)) = graft_src { + info!( + logger, + "Initializing graft by copying data from {} to {}", + src.catalog.site.namespace, + dst.catalog.site.namespace + ); + + let src_manifest_idx_and_name = src_deployment.manifest.template_idx_and_name()?; + let dst_manifest_idx_and_name = self + .load_deployment(&dst.site)? + .manifest + .template_idx_and_name()?; + + // Copy subgraph data + // We allow both not copying tables at all from the source, as well + // as adding new tables in `self`; we only need to check that tables + // that actually need to be copied from the source are compatible + // with the corresponding tables in `self` + let copy_conn = crate::copy::Connection::new( + logger, + self.pool.clone(), + src.clone(), + dst.clone(), + block.clone(), + src_manifest_idx_and_name, + dst_manifest_idx_and_name, + )?; + let status = copy_conn.copy_data()?; + if status == crate::copy::Status::Cancelled { + return Err(StoreError::Canceled); + } - let conn = self.get_conn()?; - conn.transaction(|| -> Result<(), StoreError> { - // Copy shared dynamic data sources and adjust their ID; if - // the subgraph uses private data sources, that is done by - // `copy::Connection::copy_data` since it requires access to - // the source schema which in sharded setups is only - // available while that function runs - let start = Instant::now(); - let count = dynds::shared::copy(&conn, &src.site, &dst.site, block.number)?; - info!(logger, "Copied {} dynamic data sources", count; + let conn = self.get_conn()?; + conn.transaction(|| -> Result<(), StoreError> { + // Copy shared dynamic data sources and adjust their ID; if + // the subgraph uses private data sources, that is done by + // `copy::Connection::copy_data` since it requires access to + // the source schema which in sharded setups is only + // available while that function runs + let start = Instant::now(); + let count = dynds::shared::copy(&conn, &src.site, &dst.site, block.number)?; + info!(logger, "Copied {} dynamic data sources", count; "time_ms" => start.elapsed().as_millis()); - // Copy errors across - let start = Instant::now(); - let count = deployment::copy_errors(&conn, &src.site, &dst.site, &block)?; - info!(logger, "Copied {} existing errors", count; + // Copy errors across + let start = Instant::now(); + let count = deployment::copy_errors(&conn, &src.site, &dst.site, &block)?; + info!(logger, "Copied {} existing errors", count; "time_ms" => start.elapsed().as_millis()); - catalog::copy_account_like(&conn, &src.site, &dst.site)?; - - // Rewind the subgraph so that entity versions that are - // clamped in the future (beyond `block`) become valid for - // all blocks after `block`. `revert_block` gets rid of - // everything including the block passed to it. We want to - // preserve `block` and therefore revert `block+1` - let start = Instant::now(); - let block_to_revert: BlockNumber = block - .number - .checked_add(1) - .expect("block numbers fit into an i32"); - dst.revert_block(&conn, block_to_revert)?; - info!(logger, "Rewound subgraph to block {}", block.number; + catalog::copy_account_like(&conn, &src.site, &dst.site)?; + + // Rewind the subgraph so that entity versions that are + // clamped in the future (beyond `block`) become valid for + // all blocks after `block`. `revert_block` gets rid of + // everything including the block passed to it. We want to + // preserve `block` and therefore revert `block+1` + let start = Instant::now(); + let block_to_revert: BlockNumber = block + .number + .checked_add(1) + .expect("block numbers fit into an i32"); + dst.revert_block(&conn, block_to_revert)?; + info!(logger, "Rewound subgraph to block {}", block.number; "time_ms" => start.elapsed().as_millis()); - let start = Instant::now(); - deployment::set_entity_count(&conn, &dst.site, &dst.count_query)?; - info!(logger, "Counted the entities"; + let start = Instant::now(); + deployment::set_entity_count(&conn, &dst.site, &dst.count_query)?; + info!(logger, "Counted the entities"; "time_ms" => start.elapsed().as_millis()); - // Analyze all tables for this deployment - for entity_name in dst.tables.keys() { - self.analyze_with_conn(site.cheap_clone(), entity_name.as_str(), &conn)?; - } + // Analyze all tables for this deployment + for entity_name in dst.tables.keys() { + self.analyze_with_conn(site.cheap_clone(), entity_name.as_str(), &conn)?; + } - // Set the block ptr to the graft point to signal that we successfully - // performed the graft - crate::deployment::forward_block_ptr(&conn, &dst.site.deployment, &block)?; - info!(logger, "Subgraph successfully initialized"; + // Set the block ptr to the graft point to signal that we successfully + // performed the graft + crate::deployment::forward_block_ptr(&conn, &dst.site.deployment, &block)?; + info!(logger, "Subgraph successfully initialized"; "time_ms" => start.elapsed().as_millis()); - Ok(()) - })?; - } + Ok(()) + })?; } // Make sure the block pointer is set. This is important for newly // deployed subgraphs so that we respect the 'startBlock' setting diff --git a/store/postgres/src/writable.rs b/store/postgres/src/writable.rs index 417fe368428..85aa117053a 100644 --- a/store/postgres/src/writable.rs +++ b/store/postgres/src/writable.rs @@ -9,7 +9,8 @@ use graph::components::store::ReadStore; use graph::data::subgraph::schema; use graph::env::env_var; use graph::prelude::{ - BlockNumber, Entity, MetricsRegistry, Schema, SubgraphStore as _, BLOCK_NUMBER_MAX, + BlockNumber, Entity, MetricsRegistry, Schema, SubgraphDeploymentEntity, SubgraphStore as _, + BLOCK_NUMBER_MAX, }; use graph::slog::info; use graph::util::bounded_queue::BoundedQueue; @@ -58,6 +59,10 @@ impl WritableSubgraphStore { fn layout(&self, id: &DeploymentHash) -> Result, StoreError> { self.0.layout(id) } + + fn load_deployment(&self, site: &Site) -> Result { + self.0.load_deployment(site) + } } /// Write synchronously to the actual store, i.e., once a method returns, @@ -171,7 +176,8 @@ impl SyncStore { let graft_base = match self.writable.graft_pending(&self.site.deployment)? { Some((base_id, base_ptr)) => { let src = self.store.layout(&base_id)?; - Some((src, base_ptr)) + let deployment_entity = self.store.load_deployment(&src.site)?; + Some((src, base_ptr, deployment_entity)) } None => None, }; From c4d5e14a164649eb371204a5f1c815c1ce10287a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Oct 2022 11:47:32 +0100 Subject: [PATCH 057/253] build(deps): bump tower-test from `ee82628` to `3f31ffd` (#3993) Bumps [tower-test](https://github.com/tower-rs/tower) from `ee82628` to `3f31ffd`. - [Release notes](https://github.com/tower-rs/tower/releases) - [Commits](https://github.com/tower-rs/tower/compare/ee826286fd1f994eabf14229e1e579ae29237386...3f31ffd2cf15f1e905142e5f43ab39ac995c22ed) --- updated-dependencies: - dependency-name: tower-test dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a12fab6b2..cd9a3815381 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4701,7 +4701,7 @@ dependencies = [ [[package]] name = "tower" version = "0.4.12" -source = "git+https://github.com/tower-rs/tower.git#ee826286fd1f994eabf14229e1e579ae29237386" +source = "git+https://github.com/tower-rs/tower.git#3f31ffd2cf15f1e905142e5f43ab39ac995c22ed" dependencies = [ "futures-core", "futures-util", @@ -4741,7 +4741,7 @@ checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" [[package]] name = "tower-layer" version = "0.3.1" -source = "git+https://github.com/tower-rs/tower.git#ee826286fd1f994eabf14229e1e579ae29237386" +source = "git+https://github.com/tower-rs/tower.git#3f31ffd2cf15f1e905142e5f43ab39ac995c22ed" [[package]] name = "tower-service" @@ -4752,12 +4752,12 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tower-service" version = "0.3.1" -source = "git+https://github.com/tower-rs/tower.git#ee826286fd1f994eabf14229e1e579ae29237386" +source = "git+https://github.com/tower-rs/tower.git#3f31ffd2cf15f1e905142e5f43ab39ac995c22ed" [[package]] name = "tower-test" version = "0.4.0" -source = "git+https://github.com/tower-rs/tower.git#ee826286fd1f994eabf14229e1e579ae29237386" +source = "git+https://github.com/tower-rs/tower.git#3f31ffd2cf15f1e905142e5f43ab39ac995c22ed" dependencies = [ "futures-util", "pin-project-lite", From d445e92f8b69bc38d168783d9f61ce1f7b705f44 Mon Sep 17 00:00:00 2001 From: wp-lai Date: Wed, 5 Oct 2022 18:53:24 +0800 Subject: [PATCH 058/253] docs: fix dead links (#4016) --- docs/getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 213a54073ae..e7ea53a7ca1 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -171,7 +171,7 @@ type Token @entity { This `entity` tracks a single ERC721 token on Ethereum by its ID and the current owner. The **`ID` field is required** and stores values of the ID type, which are strings. The `ID` must be a unique value so that it can be placed into the store. For an ERC721 token, the unique ID could be the token ID because that value is unique to that coin. -The exclamation mark represents the fact that that field must be set when the entity is stored in the database, i.e., it cannot be `null`. See the [Schema API](graphql-api.md#3-schema) for a complete reference on defining the schema for The Graph. +The exclamation mark represents the fact that that field must be set when the entity is stored in the database, i.e., it cannot be `null`. See the [Schema API](https://github.com/graphprotocol/docs/blob/main/pages/en/querying/graphql-api.mdx#schema) for a complete reference on defining the schema for The Graph. When you complete the schema, add its path to the top-level `schema` key in the subgraph manifest. See the code below for an example: @@ -417,7 +417,7 @@ Depending on how many events have been emitted by your smart contracts, it could ## 3 Query the Local Graph Node With the subgraph deployed to the locally running Graph Node, visit http://127.0.0.1:8000/ to open up a [GraphiQL](https://github.com/graphql/graphiql) interface where you can explore the deployed GraphQL API for the subgraph by issuing queries and viewing the schema. -We provide a few simple examples below, but please see the [Query API](graphql-api.md#1-queries) for a complete reference on how to query the subgraph's entities. +We provide a few simple examples below, but please see the [Query API](https://github.com/graphprotocol/docs/blob/main/pages/en/querying/graphql-api.mdx#queries) for a complete reference on how to query the subgraph's entities. Query the `Token` entities: ```graphql From 59eb476d23c8b4dfa4965951ae7c872824d8590a Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Fri, 7 Oct 2022 12:30:03 +0100 Subject: [PATCH 059/253] fix(server): Make '/subgraphs' return 404 (#4039) --- server/http/src/service.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index cfc560445a7..3c62fba96c9 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -254,8 +254,7 @@ where (Method::GET, path @ ["subgraphs", "id", _]) | (Method::GET, path @ ["subgraphs", "name", _]) | (Method::GET, path @ ["subgraphs", "name", _, _]) - | (Method::GET, path @ ["subgraphs", "network", _, _]) - | (Method::GET, path @ ["subgraphs"]) => { + | (Method::GET, path @ ["subgraphs", "network", _, _]) => { let dest = format!("/{}/graphql", path.join("/")); self.handle_temp_redirect(dest).boxed() } From 72443b2e9a8462a284ab433e4e67b24a8f876ab0 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Fri, 7 Oct 2022 15:06:51 +0100 Subject: [PATCH 060/253] fix(ingestor): Sanity check for receipt hash (#4042) --- chain/ethereum/src/ethereum_adapter.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/chain/ethereum/src/ethereum_adapter.rs b/chain/ethereum/src/ethereum_adapter.rs index e31d2b6662e..a6005ccd202 100644 --- a/chain/ethereum/src/ethereum_adapter.rs +++ b/chain/ethereum/src/ethereum_adapter.rs @@ -1847,7 +1847,12 @@ fn resolve_transaction_receipt( // Check if the receipt has a block hash and is for the right block. Parity nodes seem // to return receipts with no block hash when a transaction is no longer in the main // chain, so treat that case the same as a receipt being absent entirely. - if receipt.block_hash != Some(block_hash) { + // + // Also as a sanity check against provider nonsense, check that the receipt transaction + // hash and the requested transaction hash match. + if receipt.block_hash != Some(block_hash) + || transaction_hash != receipt.transaction_hash + { info!( logger, "receipt block mismatch"; "receipt_block_hash" => @@ -1855,6 +1860,7 @@ fn resolve_transaction_receipt( "block_hash" => block_hash.to_string(), "tx_hash" => transaction_hash.to_string(), + "receipt_tx_hash" => receipt.transaction_hash.to_string(), ); // If the receipt came from a different block, then the Ethereum node no longer From 2fb4a3d0622d77c2c3a40efa215c53afa6f45b07 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Thu, 6 Oct 2022 17:01:11 -0700 Subject: [PATCH 061/253] node: Change short for `graphman prune --history` to `-y` Using `-h` here conflicts with the autogenerated help option, and it seems better to keep `-h` for help even though `-y` for history is a bit goofy --- node/src/bin/manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index 786164b0476..d95b3ca21f8 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -230,7 +230,7 @@ pub enum Command { #[clap(long, short, default_value = "0.20")] prune_ratio: f64, /// How much history to keep in blocks - #[clap(long, short, default_value = "10000")] + #[clap(long, short = 'y', default_value = "10000")] history: usize, }, From 0b5248fca8321cce42a9521d8f62b142dcfbdf8b Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 4 Oct 2022 13:14:28 -0700 Subject: [PATCH 062/253] graphql: Make sure validation errors get logged with subgraph and query_id --- graphql/src/execution/query.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/graphql/src/execution/query.rs b/graphql/src/execution/query.rs index ddc09598b26..00737809ea0 100644 --- a/graphql/src/execution/query.rs +++ b/graphql/src/execution/query.rs @@ -183,8 +183,20 @@ impl Query { max_depth: u8, metrics: Arc, ) -> Result, Vec> { + let query_hash = { + let mut hasher = DefaultHasher::new(); + query.query_text.hash(&mut hasher); + query.variables_text.hash(&mut hasher); + hasher.finish() + }; + let query_id = format!("{:x}-{:x}", query.shape_hash, query_hash); + let logger = logger.new(o!( + "subgraph_id" => schema.id().clone(), + "query_id" => query_id.clone() + )); + let validation_phase_start = Instant::now(); - validate_query(logger, &query, &schema.document())?; + validate_query(&logger, &query, &schema.document())?; metrics.observe_query_validation(validation_phase_start.elapsed(), schema.id()); let mut operation = None; @@ -219,18 +231,6 @@ impl Query { } }; - let query_hash = { - let mut hasher = DefaultHasher::new(); - query.query_text.hash(&mut hasher); - query.variables_text.hash(&mut hasher); - hasher.finish() - }; - let query_id = format!("{:x}-{:x}", query.shape_hash, query_hash); - let logger = logger.new(o!( - "subgraph_id" => schema.id().clone(), - "query_id" => query_id.clone() - )); - let start = Instant::now(); let root_type = match kind { Kind::Query => schema.query_type.as_ref(), From 56e8565abe98826c0efa2ef5e5ee8ad29132a338 Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Mon, 10 Oct 2022 17:37:44 +0200 Subject: [PATCH 063/253] docker: forced enable BLOCK_INGESTOR for combined nodes (#4046) --- docker/start | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/start b/docker/start index 435fbe5429f..909e58e16a6 100755 --- a/docker/start +++ b/docker/start @@ -90,6 +90,8 @@ start_index_node() { } start_combined_node() { + # No valid reason to disable the block ingestor in this case. + export DISABLE_BLOCK_INGESTOR=false run_graph_node } From 1a71dd72cb3c3827f2fea38959f63888c2669f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Guimar=C3=A3es?= Date: Mon, 10 Oct 2022 18:31:11 -0300 Subject: [PATCH 064/253] graphman: new command to hastly delete a subgraph (#4035) --- docs/graphman.md | 289 ++++++++++++++++++ node/src/bin/manager.rs | 50 ++- node/src/manager/commands/check_blocks.rs | 24 +- node/src/manager/commands/drop.rs | 68 +++++ node/src/manager/commands/info.rs | 27 +- node/src/manager/commands/mod.rs | 1 + node/src/manager/commands/remove.rs | 5 +- .../manager/commands/unused_deployments.rs | 4 +- node/src/manager/deployment.rs | 26 ++ node/src/manager/mod.rs | 1 + node/src/manager/prompt.rs | 17 ++ 11 files changed, 462 insertions(+), 50 deletions(-) create mode 100644 docs/graphman.md create mode 100644 node/src/manager/commands/drop.rs create mode 100644 node/src/manager/prompt.rs diff --git a/docs/graphman.md b/docs/graphman.md new file mode 100644 index 00000000000..d6069ed867c --- /dev/null +++ b/docs/graphman.md @@ -0,0 +1,289 @@ +## Graphman Commands + +- [Info](#info) +- [Remove](#remove) +- [Unassign](#unassign) +- [Unused Record](#unused-record) +- [Unused Remove](#unused-remove) +- [Drop](#drop) + + +# ⌘ Info + +### SYNOPSIS + + Prints the details of a deployment + + The deployment can be specified as either a subgraph name, an IPFS hash `Qm..`, or the database + namespace `sgdNNN`. Since the same IPFS hash can be deployed in multiple shards, it is possible to + specify the shard by adding `:shard` to the IPFS hash. + + USAGE: + graphman --config info [OPTIONS] + + ARGS: + + The deployment (see above) + + OPTIONS: + -c, --current + List only current version + + -h, --help + Print help information + + -p, --pending + List only pending versions + + -s, --status + Include status information + + -u, --used + List only used (current and pending) versions + +### DESCRIPTION + +The `info` command fetches details for a given deployment from the database. + +By default, it shows the following attributes for the deployment: + +- **name** +- **status** *(`pending` or `current`)* +- **id** *(the `Qm...` identifier for the deployment's subgraph)* +- **namespace** *(The database schema which contain's that deployment data tables)* +- **shard** +- **active** *(If there are multiple entries for the same subgraph, only one of them will be active. That's the one we use for querying)* +- **chain** +- **graph node id** + +### OPTIONS + +If the `--status` option is enabled, extra attributes are also returned: + +- **synced*** *(Whether or not the subgraph has synced all the way to the current chain head)* +- **health** *(Can be either `healthy`, `unhealthy` (syncing with errors) or `failed`)* +- **latest indexed block** +- **current chain head block** + +### EXAMPLES + +Describe a deployment by its name: + + graphman --config config.toml info subgraph-name + +Describe a deployment by its hash: + + graphman --config config.toml info QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 + +Describe a deployment with extra info: + + graphman --config config.toml info QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 --status + + +# ⌘ Remove + +### SYNOPSIS + + Remove a named subgraph + + USAGE: + graphman --config remove + + ARGS: + The name of the subgraph to remove + + OPTIONS: + -h, --help Print help information + +### DESCRIPTION + +Removes the association between a subgraph name and a deployment. + +No indexed data is lost as a result of this command. + +It is used mostly for stopping query traffic based on the subgraph's name, and to release that name for +another deployment to use. + +### EXAMPLES + +Remove a named subgraph: + + graphman --config config.toml remove subgraph-name + + +# ⌘ Unassign + +#### SYNOPSIS + + Unassign a deployment + + USAGE: + graphman --config unassign + + ARGS: + The deployment (see `help info`) + + OPTIONS: + -h, --help Print help information + +#### DESCRIPTION + +Makes `graph-node` stop indexing a deployment permanently. + +No indexed data is lost as a result of this command. + +Refer to the [Maintenance Documentation](https://github.com/graphprotocol/graph-node/blob/master/docs/maintenance.md#modifying-assignments) for more details about how Graph Node manages its deployment +assignments. + +#### EXAMPLES + +Unassign a deployment by its name: + + graphman --config config.toml unassign subgraph-name + +Unassign a deployment by its hash: + + graphman --config config.toml unassign QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 + + +# ⌘ Unused Record + +### SYNOPSIS + + graphman-unused-record + Update and record currently unused deployments + + USAGE: + graphman unused record + + OPTIONS: + -h, --help Print help information + + +### DESCRIPTION + +Inspects every shard for unused deployments and registers them in the `unused_deployments` table in the +primary shard. + +No indexed data is lost as a result of this command. + +This sub-command is used as previus step towards removing all data from unused subgraphs, followed by +`graphman unused remove`. + +A deployment is unused if it fulfills all of these criteria: + +1. It is not assigned to a node. +2. It is either not marked as active or is neither the current or pending version of a subgraph. +3. It is not the source of a currently running copy operation + +### EXAMPLES + +To record all unused deployments: + + graphman --config config.toml unused record + + +# ⌘ Unused Remove + +### SYNOPSIS + + Remove deployments that were marked as unused with `record`. + + Deployments are removed in descending order of number of entities, i.e., smaller deployments are + removed before larger ones + + USAGE: + graphman unused remove [OPTIONS] + + OPTIONS: + -c, --count + How many unused deployments to remove (default: all) + + -d, --deployment + Remove a specific deployment + + -h, --help + Print help information + + -o, --older + Remove unused deployments that were recorded at least this many minutes ago + +### DESCRIPTION + +Removes from database all indexed data from deployments previously marked as unused by the `graphman unused +record` command. + +This operation is irreversible. + +### EXAMPLES + +Remove all unused deployments + + graphman --config config.toml unused remove + +Remove all unused deployments older than 12 hours (720 minutes) + + graphman --config config.toml unused remove --older 720 + +Remove a specific unused deployment + + graphman --config config.toml unused remove --deployment QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 + + +# ⌘ Drop + +### SYNOPSIS + + Delete a deployment and all it's indexed data + + The deployment can be specified as either a subgraph name, an IPFS hash `Qm..`, or the database + namespace `sgdNNN`. Since the same IPFS hash can be deployed in multiple shards, it is possible to + specify the shard by adding `:shard` to the IPFS hash. + + USAGE: + graphman --config drop [OPTIONS] + + ARGS: + + The deployment identifier + + OPTIONS: + -c, --current + Search only for current versions + + -f, --force + Skip confirmation prompt + + -h, --help + Print help information + + -p, --pending + Search only for pending versions + + -u, --used + Search only for used (current and pending) versions + +### DESCRIPTION + +Stops, unassigns and remove all data from deployments matching the search term. + +This operation is irreversible. + +This command is a combination of other graphman commands applied in sequence: + +1. `graphman info ` +2. `graphman unassign ` +3. `graphman remove ` +4. `graphman unused record` +5. `graphman unused remove ` + +### EXAMPLES + +Stop, unassign and delete all indexed data from a specific deployment by its deployment id + + graphman --config config.toml drop QmfWRZCjT8pri4Amey3e3mb2Bga75Vuh2fPYyNVnmPYL66 + + +Stop, unassign and delete all indexed data from a specific deployment by its subgraph name + + graphman --config config.toml drop autor/subgraph-name diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index d95b3ca21f8..b9e004cf4c5 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -237,6 +237,29 @@ pub enum Command { /// General database management #[clap(subcommand)] Database(DatabaseCommand), + + /// Delete a deployment and all it's indexed data + /// + /// The deployment can be specified as either a subgraph name, an IPFS + /// hash `Qm..`, or the database namespace `sgdNNN`. Since the same IPFS + /// hash can be deployed in multiple shards, it is possible to specify + /// the shard by adding `:shard` to the IPFS hash. + Drop { + /// The deployment identifier + deployment: DeploymentSearch, + /// Search only for current version + #[clap(long, short)] + current: bool, + /// Search only for pending versions + #[clap(long, short)] + pending: bool, + /// Search only for used (current and pending) versions + #[clap(long, short)] + used: bool, + /// Skip confirmation prompt + #[clap(long, short)] + force: bool, + }, } impl Command { @@ -848,7 +871,7 @@ async fn main() -> anyhow::Result<()> { } => { let count = count.unwrap_or(1_000_000); let older = older.map(|older| chrono::Duration::minutes(older as i64)); - commands::unused_deployments::remove(store, count, deployment, older) + commands::unused_deployments::remove(store, count, deployment.as_deref(), older) } } } @@ -869,7 +892,7 @@ async fn main() -> anyhow::Result<()> { } } } - Remove { name } => commands::remove::run(ctx.subgraph_store(), name), + Remove { name } => commands::remove::run(ctx.subgraph_store(), &name), Create { name } => commands::create::run(ctx.subgraph_store(), name), Unassign { deployment } => { let sender = ctx.notification_sender(); @@ -1097,6 +1120,29 @@ async fn main() -> anyhow::Result<()> { let (store, primary_pool) = ctx.store_and_primary(); commands::prune::run(store, primary_pool, deployment, history, prune_ratio).await } + Drop { + deployment, + current, + pending, + used, + force, + } => { + let sender = ctx.notification_sender(); + let (store, primary_pool) = ctx.store_and_primary(); + let subgraph_store = store.subgraph_store(); + + commands::drop::run( + primary_pool, + subgraph_store, + sender, + deployment, + current, + pending, + used, + force, + ) + .await + } } } diff --git a/node/src/manager/commands/check_blocks.rs b/node/src/manager/commands/check_blocks.rs index e5d2c0c3836..6b9fac26c52 100644 --- a/node/src/manager/commands/check_blocks.rs +++ b/node/src/manager/commands/check_blocks.rs @@ -1,3 +1,4 @@ +use crate::manager::prompt::prompt_for_confirmation; use graph::{ anyhow::{bail, ensure}, components::store::ChainStore as ChainStoreTrait, @@ -56,7 +57,11 @@ pub async fn by_range( } pub fn truncate(chain_store: Arc, skip_confirmation: bool) -> anyhow::Result<()> { - if !skip_confirmation && !helpers::prompt_for_confirmation()? { + let prompt = format!( + "This will delete all cached blocks for {}.\nProceed?", + chain_store.chain + ); + if !skip_confirmation && !prompt_for_confirmation(&prompt)? { println!("Aborting."); return Ok(()); } @@ -85,6 +90,7 @@ async fn run( mod steps { use super::*; + use futures::compat::Future01CompatExt; use graph::prelude::serde_json::{self, Value}; use json_structural_diff::{colorize as diff_to_string, JsonDiff}; @@ -187,7 +193,6 @@ mod steps { mod helpers { use super::*; use graph::prelude::hex; - use std::io::{self, Write}; /// Tries to parse a [`H256`] from a hex string. pub(super) fn parse_block_hash(hash: &str) -> anyhow::Result { @@ -196,21 +201,6 @@ mod helpers { Ok(H256::from_slice(&hash)) } - /// Asks users if they are certain about truncating the whole block cache. - pub(super) fn prompt_for_confirmation() -> anyhow::Result { - print!("This will delete all cached blocks.\nProceed? [y/N] "); - io::stdout().flush()?; - - let mut answer = String::new(); - io::stdin().read_line(&mut answer)?; - answer.make_ascii_lowercase(); - - match answer.trim() { - "y" | "yes" => Ok(true), - _ => Ok(false), - } - } - /// Convenience function for extracting values from unary sets. pub(super) fn get_single_item(name: &'static str, collection: I) -> anyhow::Result where diff --git a/node/src/manager/commands/drop.rs b/node/src/manager/commands/drop.rs new file mode 100644 index 00000000000..30d724575c5 --- /dev/null +++ b/node/src/manager/commands/drop.rs @@ -0,0 +1,68 @@ +use crate::manager::{ + deployment::{Deployment, DeploymentSearch}, + display::List, + prompt::prompt_for_confirmation, +}; +use graph::anyhow::{self, bail}; +use graph_store_postgres::{connection_pool::ConnectionPool, NotificationSender, SubgraphStore}; +use std::sync::Arc; + +/// Finds, unassigns, record and remove matching deployments. +/// +/// Asks for confirmation before removing any data. +/// This is a convenience fuction that to call a series of other graphman commands. +pub async fn run( + primary_pool: ConnectionPool, + subgraph_store: Arc, + sender: Arc, + search_term: DeploymentSearch, + current: bool, + pending: bool, + used: bool, + skip_confirmation: bool, +) -> anyhow::Result<()> { + // call `graphman info` to find matching deployments + let deployments = search_term.find(primary_pool.clone(), current, pending, used)?; + if deployments.is_empty() { + bail!("Found no deployment for search_term: {search_term}") + } else { + print_deployments(&deployments); + if !skip_confirmation && !prompt_for_confirmation("\nContinue?")? { + println!("Execution aborted by user"); + return Ok(()); + } + } + // call `graphman unassign` to stop any active deployments + crate::manager::commands::assign::unassign(primary_pool, &sender, &search_term).await?; + + // call `graphman remove` to unregister the subgraph's name + for deployment in &deployments { + crate::manager::commands::remove::run(subgraph_store.clone(), &deployment.name)?; + } + + // call `graphman unused record` to register those deployments unused + crate::manager::commands::unused_deployments::record(subgraph_store.clone())?; + + // call `graphman unused remove` to remove each deployment's data + for deployment in &deployments { + crate::manager::commands::unused_deployments::remove( + subgraph_store.clone(), + 1_000_000, + Some(&deployment.deployment), + None, + )?; + } + Ok(()) +} + +fn print_deployments(deployments: &[Deployment]) { + let mut list = List::new(vec!["name", "deployment"]); + println!("Found {} deployment(s) to remove:", deployments.len()); + for deployment in deployments { + list.append(vec![ + deployment.name.to_string(), + deployment.deployment.to_string(), + ]); + } + list.render(); +} diff --git a/node/src/manager/commands/info.rs b/node/src/manager/commands/info.rs index 19994b6ec80..76781d74d57 100644 --- a/node/src/manager/commands/info.rs +++ b/node/src/manager/commands/info.rs @@ -5,31 +5,6 @@ use graph_store_postgres::{connection_pool::ConnectionPool, Store}; use crate::manager::deployment::{Deployment, DeploymentSearch}; -fn find( - pool: ConnectionPool, - search: DeploymentSearch, - current: bool, - pending: bool, - used: bool, -) -> Result, anyhow::Error> { - let current = current || used; - let pending = pending || used; - - let deployments = search.lookup(&pool)?; - // Filter by status; if neither `current` or `pending` are set, list - // all deployments - let deployments: Vec<_> = deployments - .into_iter() - .filter(|deployment| match (current, pending) { - (true, false) => deployment.status == "current", - (false, true) => deployment.status == "pending", - (true, true) => deployment.status == "current" || deployment.status == "pending", - (false, false) => true, - }) - .collect(); - Ok(deployments) -} - pub fn run( pool: ConnectionPool, store: Option>, @@ -38,7 +13,7 @@ pub fn run( pending: bool, used: bool, ) -> Result<(), anyhow::Error> { - let deployments = find(pool, search, current, pending, used)?; + let deployments = search.find(pool, current, pending, used)?; let ids: Vec<_> = deployments.iter().map(|d| d.locator().id).collect(); let statuses = match store { Some(store) => store.status(status::Filter::DeploymentIds(ids))?, diff --git a/node/src/manager/commands/mod.rs b/node/src/manager/commands/mod.rs index 8c87bf3a68b..de7267da828 100644 --- a/node/src/manager/commands/mod.rs +++ b/node/src/manager/commands/mod.rs @@ -5,6 +5,7 @@ pub mod config; pub mod copy; pub mod create; pub mod database; +pub mod drop; pub mod index; pub mod info; pub mod listen; diff --git a/node/src/manager/commands/remove.rs b/node/src/manager/commands/remove.rs index 36d0b614a6d..e89c3642215 100644 --- a/node/src/manager/commands/remove.rs +++ b/node/src/manager/commands/remove.rs @@ -3,9 +3,8 @@ use std::sync::Arc; use graph::prelude::{anyhow, Error, SubgraphName, SubgraphStore as _}; use graph_store_postgres::SubgraphStore; -pub fn run(store: Arc, name: String) -> Result<(), Error> { - let name = SubgraphName::new(name.clone()) - .map_err(|()| anyhow!("illegal subgraph name `{}`", name))?; +pub fn run(store: Arc, name: &str) -> Result<(), Error> { + let name = SubgraphName::new(name).map_err(|()| anyhow!("illegal subgraph name `{}`", name))?; println!("Removing subgraph {}", name); store.remove_subgraph(name)?; diff --git a/node/src/manager/commands/unused_deployments.rs b/node/src/manager/commands/unused_deployments.rs index 632b2053e4e..7351d32d8c8 100644 --- a/node/src/manager/commands/unused_deployments.rs +++ b/node/src/manager/commands/unused_deployments.rs @@ -76,7 +76,7 @@ pub fn record(store: Arc) -> Result<(), Error> { pub fn remove( store: Arc, count: usize, - deployment: Option, + deployment: Option<&str>, older: Option, ) -> Result<(), Error> { let filter = match older { @@ -88,7 +88,7 @@ pub fn remove( None => unused, Some(deployment) => unused .into_iter() - .filter(|u| u.deployment.as_str() == deployment) + .filter(|u| &u.deployment == deployment) .collect::>(), }; diff --git a/node/src/manager/deployment.rs b/node/src/manager/deployment.rs index db17db10f24..f53f6d51762 100644 --- a/node/src/manager/deployment.rs +++ b/node/src/manager/deployment.rs @@ -123,6 +123,32 @@ impl DeploymentSearch { Ok(deployments) } + /// Finds all [`Deployment`]s for this [`DeploymentSearch`]. + pub fn find( + &self, + pool: ConnectionPool, + current: bool, + pending: bool, + used: bool, + ) -> Result, anyhow::Error> { + let current = current || used; + let pending = pending || used; + + let deployments = self.lookup(&pool)?; + // Filter by status; if neither `current` or `pending` are set, list + // all deployments + let deployments: Vec<_> = deployments + .into_iter() + .filter(|deployment| match (current, pending) { + (true, false) => deployment.status == "current", + (false, true) => deployment.status == "pending", + (true, true) => deployment.status == "current" || deployment.status == "pending", + (false, false) => true, + }) + .collect(); + Ok(deployments) + } + /// Finds a single deployment locator for the given deployment identifier. pub fn locate_unique(&self, pool: &ConnectionPool) -> anyhow::Result { let mut locators: Vec = HashSet::::from_iter( diff --git a/node/src/manager/mod.rs b/node/src/manager/mod.rs index 37bebf9b72c..c6630ff0a0b 100644 --- a/node/src/manager/mod.rs +++ b/node/src/manager/mod.rs @@ -9,6 +9,7 @@ pub mod catalog; pub mod commands; pub mod deployment; mod display; +pub mod prompt; /// A dummy subscription manager that always panics pub struct PanicSubscriptionManager; diff --git a/node/src/manager/prompt.rs b/node/src/manager/prompt.rs new file mode 100644 index 00000000000..be35cce4821 --- /dev/null +++ b/node/src/manager/prompt.rs @@ -0,0 +1,17 @@ +use graph::anyhow; +use std::io::{self, Write}; + +/// Asks users if they are certain about a certain action. +pub fn prompt_for_confirmation(prompt: &str) -> anyhow::Result { + print!("{prompt} [y/N] "); + io::stdout().flush()?; + + let mut answer = String::new(); + io::stdin().read_line(&mut answer)?; + answer.make_ascii_lowercase(); + + match answer.trim() { + "y" | "yes" => Ok(true), + _ => Ok(false), + } +} From 34543e58a5b56c8207878068a2c504e278b44893 Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Tue, 11 Oct 2022 11:56:45 +0200 Subject: [PATCH 065/253] docker: correctly unset DISABLE_BLOCK_INGESTOR (#4051) --- docker/start | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/start b/docker/start index 909e58e16a6..d7729a252de 100755 --- a/docker/start +++ b/docker/start @@ -91,7 +91,7 @@ start_index_node() { start_combined_node() { # No valid reason to disable the block ingestor in this case. - export DISABLE_BLOCK_INGESTOR=false + unset DISABLE_BLOCK_INGESTOR run_graph_node } From 060862bde326928aa572ab1dec27d05e518bf26c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Oct 2022 15:15:57 +0200 Subject: [PATCH 066/253] build(deps): bump url from 2.3.0 to 2.3.1 (#4023) Bumps [url](https://github.com/servo/rust-url) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/servo/rust-url/releases) - [Commits](https://github.com/servo/rust-url/compare/v2.3.0...v2.3.1) --- updated-dependencies: - dependency-name: url dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 27 ++++++++++++++++++--------- graph/Cargo.toml | 2 +- node/Cargo.toml | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd9a3815381..7183ba9261b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1306,11 +1306,10 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] @@ -2281,6 +2280,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -3102,9 +3111,9 @@ checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "petgraph" @@ -4945,12 +4954,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna", + "idna 0.3.0", "percent-encoding", ] @@ -5355,7 +5364,7 @@ dependencies = [ "futures-timer", "headers", "hex", - "idna", + "idna 0.2.3", "jsonrpc-core", "log", "once_cell", diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 5276e90f577..30a3eac4813 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -46,7 +46,7 @@ tiny-keccak = "1.5.0" tokio = { version = "1.16.1", features = ["time", "sync", "macros", "test-util", "rt-multi-thread", "parking_lot"] } tokio-stream = { version = "0.1.10", features = ["sync"] } tokio-retry = "0.3.0" -url = "2.3.0" +url = "2.3.1" prometheus = "0.13.2" priority-queue = "0.7.0" tonic = { version = "0.7.1", features = ["tls-roots","compression"] } diff --git a/node/Cargo.toml b/node/Cargo.toml index f711bf601e2..b0f44c96223 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -19,7 +19,7 @@ git-testament = "0.2" graphql-parser = "0.4.0" futures = { version = "0.3.1", features = ["compat"] } lazy_static = "1.2.0" -url = "2.3.0" +url = "2.3.1" crossbeam-channel = "0.5.5" graph = { path = "../graph" } graph-core = { path = "../core" } From 3aa17b2b48a8152f41720d5d89f470bd3ce313b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Oct 2022 15:16:34 +0200 Subject: [PATCH 067/253] build(deps): bump openssl from 0.10.41 to 0.10.42 (#4022) Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.41 to 0.10.42. - [Release notes](https://github.com/sfackler/rust-openssl/releases) - [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.41...openssl-v0.10.42) --- updated-dependencies: - dependency-name: openssl dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- store/postgres/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7183ba9261b..335860af436 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2947,9 +2947,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.41" +version = "0.10.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2979,9 +2979,9 @@ checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" [[package]] name = "openssl-sys" -version = "0.9.75" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg", "cc", diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index fad076bc52f..ef30eb85531 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -21,7 +21,7 @@ lazy_static = "1.1" lru_time_cache = "0.11" maybe-owned = "0.3.4" postgres = "0.19.1" -openssl = "0.10.41" +openssl = "0.10.42" postgres-openssl = "0.5.0" rand = "0.8.4" serde = "1.0" From 6d1622a731fc65d590e06fe481159bbe5e915314 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Oct 2022 11:02:52 +0100 Subject: [PATCH 068/253] build(deps): bump proc-macro2 from 1.0.43 to 1.0.46 (#4020) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.43 to 1.0.46. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.43...1.0.46) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- runtime/derive/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 335860af436..a112fa36d2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3351,9 +3351,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] diff --git a/runtime/derive/Cargo.toml b/runtime/derive/Cargo.toml index 2ae12aef900..6bc902087a8 100644 --- a/runtime/derive/Cargo.toml +++ b/runtime/derive/Cargo.toml @@ -9,5 +9,5 @@ proc-macro = true [dependencies] syn = { version = "1.0.98", features = ["full"] } quote = "1.0" -proc-macro2 = "1.0.43" +proc-macro2 = "1.0.46" heck = "0.4" From fdb98063f91a82ef530f4b83cadde68fedab9913 Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Wed, 12 Oct 2022 14:16:11 +0200 Subject: [PATCH 069/253] news: release notes for `v0.28.1` and `v0.28.2` patches (#4052) * Release 0.28.1 * news: release v0.28.1 * Release 0.28.2 * news: release v0.28.2 --- Cargo.lock | 44 ++++++++++++++++++------------------ NEWS.md | 42 +++++++++++++++++++++------------- chain/arweave/Cargo.toml | 2 +- chain/common/Cargo.toml | 2 +- chain/cosmos/Cargo.toml | 2 +- chain/ethereum/Cargo.toml | 2 +- chain/near/Cargo.toml | 2 +- chain/substreams/Cargo.toml | 2 +- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- graphql/Cargo.toml | 2 +- mock/Cargo.toml | 2 +- node/Cargo.toml | 2 +- runtime/derive/Cargo.toml | 2 +- runtime/test/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- server/http/Cargo.toml | 2 +- server/index-node/Cargo.toml | 2 +- server/json-rpc/Cargo.toml | 2 +- server/metrics/Cargo.toml | 2 +- server/websocket/Cargo.toml | 2 +- store/postgres/Cargo.toml | 2 +- store/test-store/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- 24 files changed, 70 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a112fa36d2a..a2679e7355d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1508,7 +1508,7 @@ dependencies = [ [[package]] name = "graph" -version = "0.28.0" +version = "0.28.2" dependencies = [ "Inflector", "anyhow", @@ -1573,7 +1573,7 @@ dependencies = [ [[package]] name = "graph-chain-arweave" -version = "0.28.0" +version = "0.28.2" dependencies = [ "base64-url", "diesel", @@ -1589,7 +1589,7 @@ dependencies = [ [[package]] name = "graph-chain-common" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "heck 0.4.0", @@ -1599,7 +1599,7 @@ dependencies = [ [[package]] name = "graph-chain-cosmos" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "graph", @@ -1615,7 +1615,7 @@ dependencies = [ [[package]] name = "graph-chain-ethereum" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "base64", @@ -1641,7 +1641,7 @@ dependencies = [ [[package]] name = "graph-chain-near" -version = "0.28.0" +version = "0.28.2" dependencies = [ "base64", "diesel", @@ -1656,7 +1656,7 @@ dependencies = [ [[package]] name = "graph-chain-substreams" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "async-stream", @@ -1682,7 +1682,7 @@ dependencies = [ [[package]] name = "graph-core" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "async-stream", @@ -1719,7 +1719,7 @@ dependencies = [ [[package]] name = "graph-graphql" -version = "0.28.0" +version = "0.28.2" dependencies = [ "Inflector", "anyhow", @@ -1741,14 +1741,14 @@ dependencies = [ [[package]] name = "graph-mock" -version = "0.28.0" +version = "0.28.2" dependencies = [ "graph", ] [[package]] name = "graph-node" -version = "0.28.0" +version = "0.28.2" dependencies = [ "clap", "crossbeam-channel", @@ -1786,7 +1786,7 @@ dependencies = [ [[package]] name = "graph-runtime-derive" -version = "0.28.0" +version = "0.28.2" dependencies = [ "heck 0.4.0", "proc-macro2", @@ -1796,7 +1796,7 @@ dependencies = [ [[package]] name = "graph-runtime-test" -version = "0.28.0" +version = "0.28.2" dependencies = [ "graph", "graph-chain-ethereum", @@ -1812,7 +1812,7 @@ dependencies = [ [[package]] name = "graph-runtime-wasm" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "async-trait", @@ -1838,7 +1838,7 @@ dependencies = [ [[package]] name = "graph-server-http" -version = "0.28.0" +version = "0.28.2" dependencies = [ "futures 0.1.31", "graph", @@ -1852,7 +1852,7 @@ dependencies = [ [[package]] name = "graph-server-index-node" -version = "0.28.0" +version = "0.28.2" dependencies = [ "blake3 1.3.1", "either", @@ -1872,7 +1872,7 @@ dependencies = [ [[package]] name = "graph-server-json-rpc" -version = "0.28.0" +version = "0.28.2" dependencies = [ "graph", "jsonrpsee", @@ -1881,7 +1881,7 @@ dependencies = [ [[package]] name = "graph-server-metrics" -version = "0.28.0" +version = "0.28.2" dependencies = [ "graph", "http", @@ -1892,7 +1892,7 @@ dependencies = [ [[package]] name = "graph-server-websocket" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "futures 0.1.31", @@ -1908,7 +1908,7 @@ dependencies = [ [[package]] name = "graph-store-postgres" -version = "0.28.0" +version = "0.28.2" dependencies = [ "Inflector", "anyhow", @@ -1948,7 +1948,7 @@ dependencies = [ [[package]] name = "graph-tests" -version = "0.28.0" +version = "0.28.2" dependencies = [ "anyhow", "async-stream", @@ -4362,7 +4362,7 @@ dependencies = [ [[package]] name = "test-store" -version = "0.28.0" +version = "0.28.2" dependencies = [ "diesel", "graph", diff --git a/NEWS.md b/NEWS.md index e526944dd96..f6b131964b0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,16 @@ ## Unreleased +## v0.28.2 + +**Indexers are advised to migrate to `v0.28.2`** and entirely bypass `v0.28.0` and `v0.28.1`. + +Fixed a bug which would cause subgraphs to stop syncing under some `graph-node` deployment configurations. [#4046](https://github.com/graphprotocol/graph-node/pull/4046), [#4051](https://github.com/graphprotocol/graph-node/pull/4051) + +## v0.28.1 + +Yanked. Please migrate to `v0.28.2`. + ## v0.28.0 #### Upgrade notes @@ -52,22 +62,22 @@ ### Dependency updates -| Dependency | PR(s) | Old version | Current version | -|----|----|------|----| -| `serde_yaml` | [#3746](https://github.com/graphprotocol/graph-node/pull/3746) | `v0.8.24` | `v0.8.26` | -| `web3` | [#3806](https://github.com/graphprotocol/graph-node/pull/3806) | `2760dbd` | `7f8eb6d` | -| `clap` | [#3794](https://github.com/graphprotocol/graph-node/pull/3794), [#3848](https://github.com/graphprotocol/graph-node/pull/3848), [#3931](https://github.com/graphprotocol/graph-node/pull/3931) | `v3.2.8` | `3.2.21` | -| `cid` | [#3824](https://github.com/graphprotocol/graph-node/pull/3824) | `v0.8.5` | `v0.8.6` | -| `anyhow` | [#3826](https://github.com/graphprotocol/graph-node/pull/3826), [#3841](https://github.com/graphprotocol/graph-node/pull/3841), [#3865](https://github.com/graphprotocol/graph-node/pull/3865), [#3932](https://github.com/graphprotocol/graph-node/pull/3932) | `v1.0.57` | `1.0.65` | -| `chrono` | [#3827](https://github.com/graphprotocol/graph-node/pull/3827), [#3849](https://github.com/graphprotocol/graph-node/pull/3839), [#3868](https://github.com/graphprotocol/graph-node/pull/3868) | `v0.4.19` | `v0.4.22` | -| `proc-macro2` | [#3845](https://github.com/graphprotocol/graph-node/pull/3845) | `v1.0.40` | `1.0.43` | -| `ethabi` | [#3847](https://github.com/graphprotocol/graph-node/pull/3847) | `v17.1.0` | `v17.2.0` | -| `once_cell` | [#3870](https://github.com/graphprotocol/graph-node/pull/3870) | `v1.13.0` | `v1.13.1` | -| `either` | [#3869](https://github.com/graphprotocol/graph-node/pull/3869) | `v1.7.0` | `v1.8.0` | -| `sha2` | [#3904](https://github.com/graphprotocol/graph-node/pull/3904) | `v0.10.2` | `v0.10.5` | -| `mockall` | [#3776](https://github.com/graphprotocol/graph-node/pull/3776) | `v0.9.1` | removed | -| `croosbeam` | [#3772](https://github.com/graphprotocol/graph-node/pull/3772) | `v0.8.1` | `v0.8.2` | -| `async-recursion` | [#3873](https://github.com/graphprotocol/graph-node/pull/3873) | none | `v1.0.0` | +| Dependency | PR(s) | Old version | Current version | +| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | --------------- | +| `serde_yaml` | [#3746](https://github.com/graphprotocol/graph-node/pull/3746) | `v0.8.24` | `v0.8.26` | +| `web3` | [#3806](https://github.com/graphprotocol/graph-node/pull/3806) | `2760dbd` | `7f8eb6d` | +| `clap` | [#3794](https://github.com/graphprotocol/graph-node/pull/3794), [#3848](https://github.com/graphprotocol/graph-node/pull/3848), [#3931](https://github.com/graphprotocol/graph-node/pull/3931) | `v3.2.8` | `3.2.21` | +| `cid` | [#3824](https://github.com/graphprotocol/graph-node/pull/3824) | `v0.8.5` | `v0.8.6` | +| `anyhow` | [#3826](https://github.com/graphprotocol/graph-node/pull/3826), [#3841](https://github.com/graphprotocol/graph-node/pull/3841), [#3865](https://github.com/graphprotocol/graph-node/pull/3865), [#3932](https://github.com/graphprotocol/graph-node/pull/3932) | `v1.0.57` | `1.0.65` | +| `chrono` | [#3827](https://github.com/graphprotocol/graph-node/pull/3827), [#3849](https://github.com/graphprotocol/graph-node/pull/3839), [#3868](https://github.com/graphprotocol/graph-node/pull/3868) | `v0.4.19` | `v0.4.22` | +| `proc-macro2` | [#3845](https://github.com/graphprotocol/graph-node/pull/3845) | `v1.0.40` | `1.0.43` | +| `ethabi` | [#3847](https://github.com/graphprotocol/graph-node/pull/3847) | `v17.1.0` | `v17.2.0` | +| `once_cell` | [#3870](https://github.com/graphprotocol/graph-node/pull/3870) | `v1.13.0` | `v1.13.1` | +| `either` | [#3869](https://github.com/graphprotocol/graph-node/pull/3869) | `v1.7.0` | `v1.8.0` | +| `sha2` | [#3904](https://github.com/graphprotocol/graph-node/pull/3904) | `v0.10.2` | `v0.10.5` | +| `mockall` | [#3776](https://github.com/graphprotocol/graph-node/pull/3776) | `v0.9.1` | removed | +| `croosbeam` | [#3772](https://github.com/graphprotocol/graph-node/pull/3772) | `v0.8.1` | `v0.8.2` | +| `async-recursion` | [#3873](https://github.com/graphprotocol/graph-node/pull/3873) | none | `v1.0.0` | -- This release includes a **determinism fix** that should affect very few subgraphs on the network (currently only two). There was an issue that if a subgraph manifest had one data source with no contract address, listening to the same events or calls of another data source that has a specified address, the handlers for those would be called twice. With the fix, this will happen no more, the handler will be called just once like it should. - - The two affected deployments are: `Qmccst5mbV5a6vT6VvJMLPKMAA1VRgT6NGbxkLL8eDRsE7` and `Qmd9nZKCH8UZU1pBzk7G8ECJr3jX3a2vAf3vowuTwFvrQg`; - - Here's an example [manifest](https://ipfs.io/ipfs/Qmd9nZKCH8UZU1pBzk7G8ECJr3jX3a2vAf3vowuTwFvrQg), taking a look at the data sources of name `ERC721` and `CryptoKitties`, both listen to the `Transfer(...)` event. Considering a block where there's only one occurence of this event, `graph-node` would duplicate it and call `handleTransfer` twice. Now this is fixed and it will be called only once per event/call that happened on chain. - - In the case you're indexing one of those, you should first upgrade the `graph-node` version, then rewind the affected subgraphs to the smallest `startBlock` of their subgraph manifest. To achieve that the `graphman rewind` CLI command can be used. -- We now check that the database uses the `C` locale and `UTF8` character encoding. For new installations, `graph-node` will panic on startup if the database uses any other locale. The easiest way to make sure this check passes is to create the database cluster with `initdb -E UTF8 --locale C`. We will provide instructions on migrating existing installations in the future. +**Full Changelog**: https://github.com/graphprotocol/graph-node/compare/v0.28.2...v0.29.0 ## v0.28.2 From 191d7971fe0d6d875a42718e3c3752bd44dbaa92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:02:50 +0100 Subject: [PATCH 226/253] build(deps): bump proc-macro2 from 1.0.49 to 1.0.50 (#4295) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.49 to 1.0.50. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.49...1.0.50) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- runtime/derive/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27dfe9348b4..15891ccbbe5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3346,9 +3346,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] diff --git a/runtime/derive/Cargo.toml b/runtime/derive/Cargo.toml index 589a8c61fac..eb3f8e4fb02 100644 --- a/runtime/derive/Cargo.toml +++ b/runtime/derive/Cargo.toml @@ -9,5 +9,5 @@ proc-macro = true [dependencies] syn = { version = "1.0.98", features = ["full"] } quote = "1.0" -proc-macro2 = "1.0.49" +proc-macro2 = "1.0.50" heck = "0.4" From 8f5785d7fde1af7d010cc9b8dd0c9b4c94e5892c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:03:45 +0100 Subject: [PATCH 227/253] build(deps): bump cid from 0.9.0 to 0.10.1 (#4289) Bumps [cid](https://github.com/multiformats/rust-cid) from 0.9.0 to 0.10.1. - [Release notes](https://github.com/multiformats/rust-cid/releases) - [Changelog](https://github.com/multiformats/rust-cid/blob/master/CHANGELOG.md) - [Commits](https://github.com/multiformats/rust-cid/compare/v0.9.0...v0.10.1) --- updated-dependencies: - dependency-name: cid dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 19 +++++++++++++++---- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15891ccbbe5..e1b253d14db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,13 +481,13 @@ dependencies = [ [[package]] name = "cid" -version = "0.9.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b68e3193982cd54187d71afdb2a271ad4cf8af157858e9cb911b91321de143" +checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3" dependencies = [ "core2", "multibase", - "multihash", + "multihash 0.18.0", "serde", "unsigned-varint", ] @@ -2791,7 +2791,7 @@ dependencies = [ "byteorder", "data-encoding", "multibase", - "multihash", + "multihash 0.17.0", "percent-encoding", "serde", "static_assertions", @@ -2815,6 +2815,17 @@ name = "multihash" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "multihash-derive", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e5d911412e631e1de11eb313e4dd71f73fd964401102aab23d6c8327c431ba" dependencies = [ "blake2b_simd", "blake2s_simd", diff --git a/core/Cargo.toml b/core/Cargo.toml index 030aaab5c24..8936a94ef55 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -27,7 +27,7 @@ serde_yaml = "0.8" # Switch to crates.io once tower 0.5 is released tower = { git = "https://github.com/tower-rs/tower.git", features = ["full"] } graph-runtime-wasm = { path = "../runtime/wasm" } -cid = "0.9.0" +cid = "0.10.1" anyhow = "1.0" [dev-dependencies] diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 16bb40675f5..91c766ce4e1 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -10,7 +10,7 @@ async-stream = "0.3" atomic_refcell = "0.1.8" bigdecimal = { version = "0.1.0", features = ["serde"] } bytes = "1.0.1" -cid = "0.9.0" +cid = "0.10.1" diesel = { version = "1.4.8", features = ["postgres", "serde_json", "numeric", "r2d2", "chrono"] } diesel_derives = "1.4" chrono = "0.4.23" diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 52a5f00e800..b0893f97ba9 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -31,5 +31,5 @@ anyhow = "1.0.68" lazy_static = "1.4.0" tokio-stream = "0.1" serde_yaml = "0.8" -cid = "0.9.0" +cid = "0.10.1" graph-chain-near = { path = "../chain/near" } From 5945af0d21b169d322187494a591850b7af1e667 Mon Sep 17 00:00:00 2001 From: Filippo Neysofu Costa Date: Thu, 19 Jan 2023 13:42:22 +0100 Subject: [PATCH 228/253] Cache recent blocks in memory to reduce load on the db (#4215) * store: cache recent blocks in memory * store: refactor RecentBlocksCache's Inner methods * store: fix ancestor/child naming in RecentBlocksCache * store: use a BTreeMap for RecentCachedBlocks This commit significatly alters the design of RecentCachedBlocks. The most prominent changes are: 1. We don't require a `.set_chain_head` call anymore. Block insertion and chain head update attempt are now the same thing. 2. We don't evict all items in the cache anymore every time the chain head advances. 3. Unlike the previous data structure, we are now limited to storing a contiguous range of blocks in the cache. This is not really a drawback (as the cache contents will usually be identical, i.e. the last N blocks before the chain head), but it's worth pointing out. * rust: upgrade to 1.66 * store: readability improvements to RecentBlocksCache * store: fix RecentBlocksCache for NEAR This commit removes the requirement of continuous block number ranges for RecentBlocksCache. Two reasons for that: - NEAR doesn't have continuous block number ranges, so the previous cache design would have limited effectiveness for NEAR chains. - Parent hash comparisons are actually enough to uphold all invariants, so block number checks were unnecessary in the first place. The actual code changes are small and mostly limited to `insert_block`. --- graph/src/env/store.rs | 6 + rust-toolchain.toml | 2 +- store/postgres/src/block_store.rs | 3 +- store/postgres/src/chain_store.rs | 243 ++++++++++++++++++++++++++---- 4 files changed, 224 insertions(+), 30 deletions(-) diff --git a/graph/src/env/store.rs b/graph/src/env/store.rs index 4bfe0d0616f..1132333c0dd 100644 --- a/graph/src/env/store.rs +++ b/graph/src/env/store.rs @@ -66,6 +66,9 @@ pub struct EnvVarsStore { /// Set by the environment variable `GRAPH_REMOVE_UNUSED_INTERVAL` /// (expressed in minutes). The default value is 360 minutes. pub remove_unused_interval: chrono::Duration, + /// Set by the environment variable + /// `GRAPH_STORE_RECENT_BLOCKS_CACHE_CAPACITY`. The default value is 10 blocks. + pub recent_blocks_cache_capacity: usize, // These should really be set through the configuration file, especially for // `GRAPH_STORE_CONNECTION_MIN_IDLE` and @@ -123,6 +126,7 @@ impl From for EnvVarsStore { remove_unused_interval: chrono::Duration::minutes( x.remove_unused_interval_in_minutes as i64, ), + recent_blocks_cache_capacity: x.recent_blocks_cache_capacity, connection_timeout: Duration::from_millis(x.connection_timeout_in_millis), connection_min_idle: x.connection_min_idle, connection_idle_timeout: Duration::from_secs(x.connection_idle_timeout_in_secs), @@ -158,6 +162,8 @@ pub struct InnerStore { connection_try_always: EnvVarBoolean, #[envconfig(from = "GRAPH_REMOVE_UNUSED_INTERVAL", default = "360")] remove_unused_interval_in_minutes: u64, + #[envconfig(from = "GRAPH_STORE_RECENT_BLOCKS_CACHE_CAPACITY", default = "10")] + recent_blocks_cache_capacity: usize, // These should really be set through the configuration file, especially for // `GRAPH_STORE_CONNECTION_MIN_IDLE` and diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 68249258292..f0d51c34aef 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.65.0" +channel = "1.66.0" profile = "default" diff --git a/store/postgres/src/block_store.rs b/store/postgres/src/block_store.rs index b31281abf56..79bc4db40f7 100644 --- a/store/postgres/src/block_store.rs +++ b/store/postgres/src/block_store.rs @@ -8,7 +8,7 @@ use std::{ use graph::{ blockchain::ChainIdentifier, components::store::BlockStore as BlockStoreTrait, - prelude::{error, warn, BlockNumber, BlockPtr, Logger}, + prelude::{error, warn, BlockNumber, BlockPtr, Logger, ENV_VARS}, }; use graph::{ constraint_violation, @@ -372,6 +372,7 @@ impl BlockStore { status, sender, pool, + ENV_VARS.store.recent_blocks_cache_capacity, ); if create { store.create(&ident)?; diff --git a/store/postgres/src/chain_store.rs b/store/postgres/src/chain_store.rs index 26225820d5a..fcf2e22a8b9 100644 --- a/store/postgres/src/chain_store.rs +++ b/store/postgres/src/chain_store.rs @@ -3,13 +3,13 @@ use diesel::prelude::*; use diesel::r2d2::{ConnectionManager, PooledConnection}; use diesel::sql_types::Text; use diesel::{insert_into, update}; +use graph::parking_lot::RwLock; use std::{ collections::HashMap, convert::{TryFrom, TryInto}, iter::FromIterator, sync::Arc, - time::Duration, }; use graph::blockchain::{Block, BlockHash, ChainIdentifier}; @@ -20,9 +20,9 @@ use graph::prelude::{ BlockNumber, BlockPtr, CachedEthereumCall, CancelableError, ChainStore as ChainStoreTrait, Error, EthereumCallCache, StoreError, }; -use graph::util::timed_cache::TimedCache; use graph::{constraint_violation, ensure}; +use self::recent_blocks_cache::RecentBlocksCache; use crate::{ block_store::ChainStatus, chain_head_listener::ChainHeadUpdateSender, connection_pool::ConnectionPool, @@ -71,6 +71,7 @@ mod data { }; use std::fmt; use std::iter::FromIterator; + use std::str::FromStr; use std::{convert::TryFrom, io::Write}; use crate::transaction_receipt::RawTransactionReceipt; @@ -795,8 +796,8 @@ mod data { conn: &PgConnection, block_ptr: BlockPtr, offset: BlockNumber, - ) -> Result, Error> { - let data = match self { + ) -> Result, Error> { + let data_and_hash = match self { Storage::Shared => { const ANCESTOR_SQL: &str = " with recursive ancestors(block_hash, block_offset) as ( @@ -821,12 +822,13 @@ mod data { match hash { None => None, - Some(hash) => Some( + Some(hash) => Some(( b::table - .filter(b::hash.eq(hash.hash)) + .filter(b::hash.eq(&hash.hash)) .select(b::data) .first::(conn)?, - ), + BlockHash::from_str(&hash.hash)?, + )), } } Storage::Private(Schema { blocks, .. }) => { @@ -854,13 +856,14 @@ mod data { .optional()?; match hash { None => None, - Some(hash) => Some( + Some(hash) => Some(( blocks .table() - .filter(blocks.hash().eq(hash.hash)) + .filter(blocks.hash().eq(&hash.hash)) .select(blocks.data()) .first::(conn)?, - ), + BlockHash::from(hash.hash), + )), } } }; @@ -871,15 +874,20 @@ mod data { // has a 'block' entry // // see also 7736e440-4c6b-11ec-8c4d-b42e99f52061 - let data = { + let data_and_ptr = { use graph::prelude::serde_json::json; - data.map(|data| match data.get("block") { - Some(_) => data, - None => json!({ "block": data, "transaction_receipts": [] }), + data_and_hash.map(|(data, hash)| { + ( + match data.get("block") { + Some(_) => data, + None => json!({ "block": data, "transaction_receipts": [] }), + }, + BlockPtr::new(hash, block_ptr.number - offset), + ) }) }; - Ok(data) + Ok(data_and_ptr) } pub(super) fn delete_blocks_before( @@ -1344,7 +1352,13 @@ pub struct ChainStore { genesis_block_ptr: BlockPtr, status: ChainStatus, chain_head_update_sender: ChainHeadUpdateSender, - block_cache: TimedCache<&'static str, BlockPtr>, + // TODO: We currently only use this cache for + // [`ChainStore::ancestor_block`], but it could very well be expanded to + // also track the network's chain head and generally improve its hit rate. + // It is, however, quite challenging to keep the cache perfectly consistent + // with the database and to correctly implement invalidation. So, a + // conservative approach is acceptable. + recent_blocks_cache: RecentBlocksCache, } impl ChainStore { @@ -1355,6 +1369,7 @@ impl ChainStore { status: ChainStatus, chain_head_update_sender: ChainHeadUpdateSender, pool: ConnectionPool, + recent_blocks_cache_capacity: usize, ) -> Self { ChainStore { pool, @@ -1363,7 +1378,7 @@ impl ChainStore { genesis_block_ptr: BlockPtr::new(net_identifier.genesis_block_hash.clone(), 0), status, chain_head_update_sender, - block_cache: TimedCache::new(Duration::from_secs(5)), + recent_blocks_cache: RecentBlocksCache::new(recent_blocks_cache_capacity), } } @@ -1484,6 +1499,12 @@ impl ChainStoreTrait for ChainStore { } async fn upsert_block(&self, block: Arc) -> Result<(), Error> { + // We should always have the parent block available to us at this point. + if let Some(parent_hash) = block.parent_hash() { + self.recent_blocks_cache + .insert_block(block.ptr(), block.data().ok(), parent_hash); + } + let pool = self.pool.clone(); let network = self.chain.clone(); let storage = self.storage.clone(); @@ -1596,10 +1617,6 @@ impl ChainStoreTrait for ChainStore { _ => unreachable!(), }) .and_then(|opt: Option| opt) - .map(|head| { - self.block_cache.set("head", Arc::new(head.clone())); - head - }) }) .map_err(|e| CancelableError::from(StoreError::from(e))) }) @@ -1607,8 +1624,8 @@ impl ChainStoreTrait for ChainStore { } async fn cached_head_ptr(self: Arc) -> Result, Error> { - match self.block_cache.get("head") { - Some(head) => Ok(Some(head.as_ref().clone())), + match self.recent_blocks_cache.chain_head_ptr() { + Some(head) => Ok(Some(head)), None => self.chain_head_ptr().await, } } @@ -1686,15 +1703,24 @@ impl ChainStoreTrait for ChainStore { block_ptr.hash_hex() ); + // Check the local cache first. + if let Some(data) = self.recent_blocks_cache.get_block(&block_ptr, offset) { + return Ok(data.1.clone()); + } + + let block_ptr_clone = block_ptr.clone(); + let chain_store = self.cheap_clone(); Ok(self - .cheap_clone() .pool .with_conn(move |conn, _| { - self.storage - .ancestor_block(conn, block_ptr, offset) - .map_err(|e| CancelableError::from(StoreError::from(e))) + chain_store + .storage + .ancestor_block(conn, block_ptr_clone, offset) + .map_err(StoreError::from) + .map_err(CancelableError::from) }) - .await?) + .await? + .map(|b| b.0)) } fn cleanup_cached_blocks( @@ -1709,6 +1735,8 @@ impl ChainStoreTrait for ChainStore { block: i32, } + self.recent_blocks_cache.clear(); + // Remove all blocks from the cache that are behind the slowest // subgraph's head block, but retain the genesis block. We stay // behind the slowest subgraph so that we do not interfere with its @@ -1813,6 +1841,165 @@ impl ChainStoreTrait for ChainStore { } } +mod recent_blocks_cache { + use super::*; + use std::collections::BTreeMap; + + struct CachedBlock { + ptr: BlockPtr, + data: Option, + parent_hash: BlockHash, + } + + struct Inner { + // Note: we only ever store blocks in this cache that have a continuous + // line of ancestry between each other. Line of ancestry is verified by + // comparing parent hashes. Because of NEAR, however, we cannot + // guarantee that there are no block number gaps, as block numbers are + // not strictly continuous: + // #14 (Hash ABC1, Parent XX) -> #17 (Hash EBD2, Parent ABC1) + blocks: BTreeMap, + // We only store these many blocks. + capacity: usize, + } + + impl Inner { + fn get_block( + &self, + child: &BlockPtr, + offset: BlockNumber, + ) -> Option<(&BlockPtr, Option<&json::Value>)> { + // Before we can go find the ancestor, we need to make sure that + // we're looking for the ancestor of the right block, i.e. check if + // the hash (and number) of the child matches. + let child_is_cached = &self.blocks.get(&child.number)?.ptr == child; + + if child_is_cached { + let ancestor_block_number = child.number - offset; + let block = self.blocks.get(&ancestor_block_number)?; + Some((&block.ptr, block.data.as_ref())) + } else { + None + } + } + + fn chain_head(&self) -> Option<&BlockPtr> { + self.blocks.last_key_value().map(|b| &b.1.ptr) + } + + fn earliest_block(&self) -> Option<&CachedBlock> { + self.blocks.first_key_value().map(|b| b.1) + } + + fn evict_if_necessary(&mut self) { + while self.blocks.len() > self.capacity { + self.blocks.pop_first(); + } + } + + fn insert_block( + &mut self, + ptr: BlockPtr, + data: Option, + parent_hash: BlockHash, + ) { + fn is_parent_of(parent: &BlockPtr, child: &CachedBlock) -> bool { + child.parent_hash == parent.hash + } + + let block = CachedBlock { + ptr, + data, + parent_hash, + }; + + let Some(chain_head) = self.chain_head() else { + // We don't have anything in the cache, so we're free to store + // everything we want. + self.blocks.insert(block.ptr.number, block); + return; + }; + + if is_parent_of(chain_head, &block) { + // We have a new chain head that is a direct child of our + // previous chain head, so we get to keep all items in the + // cache. + self.blocks.insert(block.ptr.number, block); + } else if block.ptr.number > chain_head.number { + // We have a new chain head, but it's not a direct child of + // our previous chain head. This means that we must + // invalidate all the items in the cache before inserting + // this block. + self.blocks.clear(); + self.blocks.insert(block.ptr.number, block); + } else { + // Unwrap: we have checked already that the cache is not empty, + // at the beginning of this function body. + let earliest_block = self.earliest_block().unwrap(); + // Let's check if this is the parent of the earliest block in the + // cache. + if is_parent_of(&block.ptr, earliest_block) { + self.blocks.insert(block.ptr.number, block); + } + } + + self.evict_if_necessary(); + } + } + + /// We cache the most recent blocks in memory to avoid overloading the + /// database with unnecessary queries close to the chain head. We invalidate + /// blocks whenever the chain head advances. + pub struct RecentBlocksCache { + // We protect everything with a global `RwLock` to avoid data races. Ugly... + inner: RwLock, + } + + impl RecentBlocksCache { + pub fn new(capacity: usize) -> Self { + RecentBlocksCache { + inner: RwLock::new(Inner { + blocks: BTreeMap::new(), + capacity, + }), + } + } + + pub fn chain_head_ptr(&self) -> Option { + let inner = self.inner.read(); + inner.chain_head().cloned() + } + + pub fn clear(&self) { + self.inner.write().blocks.clear() + } + + pub fn get_block( + &self, + child: &BlockPtr, + offset: BlockNumber, + ) -> Option<(BlockPtr, Option)> { + self.inner + .read() + .get_block(child, offset) + .map(|b| (b.0.clone(), b.1.cloned())) + } + + /// Tentatively caches the `ancestor` of a [`BlockPtr`] (`child`), together with + /// its associated `data`. Note that for this to work, `child` must be + /// in the cache already. The first block in the cache should be + /// inserted via [`RecentBlocksCache::set_chain_head`]. + pub fn insert_block( + &self, + ptr: BlockPtr, + data: Option, + parent_hash: BlockHash, + ) { + self.inner.write().insert_block(ptr, data, parent_hash) + } + } +} + fn try_parse_timestamp(ts: Option) -> Result, StoreError> { let ts = match ts { Some(str) => str, From 32176005afcdc8b767eea64150784e1037b2bf8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:24:22 +0000 Subject: [PATCH 229/253] build(deps): bump tower-test from `c9d84cd` to `b01bb12` (#4296) Bumps [tower-test](https://github.com/tower-rs/tower) from `c9d84cd` to `b01bb12`. - [Release notes](https://github.com/tower-rs/tower/releases) - [Commits](https://github.com/tower-rs/tower/compare/c9d84cde0c9a23e1d2d5b5ae7ae432629712658b...b01bb12ddd3178f2e324839caab3316af593dc6d) --- updated-dependencies: - dependency-name: tower-test dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1b253d14db..35e03264ca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4726,7 +4726,7 @@ dependencies = [ [[package]] name = "tower" version = "0.4.12" -source = "git+https://github.com/tower-rs/tower.git#c9d84cde0c9a23e1d2d5b5ae7ae432629712658b" +source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" dependencies = [ "futures-core", "futures-util", @@ -4734,6 +4734,7 @@ dependencies = [ "indexmap", "pin-project-lite", "slab", + "sync_wrapper", "tokio", "tokio-util 0.7.1", "tower-layer 0.3.1", @@ -4783,7 +4784,7 @@ dependencies = [ [[package]] name = "tower-layer" version = "0.3.1" -source = "git+https://github.com/tower-rs/tower.git#c9d84cde0c9a23e1d2d5b5ae7ae432629712658b" +source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" [[package]] name = "tower-layer" @@ -4800,12 +4801,12 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tower-service" version = "0.3.1" -source = "git+https://github.com/tower-rs/tower.git#c9d84cde0c9a23e1d2d5b5ae7ae432629712658b" +source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" [[package]] name = "tower-test" version = "0.4.0" -source = "git+https://github.com/tower-rs/tower.git#c9d84cde0c9a23e1d2d5b5ae7ae432629712658b" +source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" dependencies = [ "futures-util", "pin-project-lite", From e8d5a576168872e5a2c09c49297ede3987e2578d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jan 2023 16:39:39 +0000 Subject: [PATCH 230/253] build(deps): bump prost from 0.11.5 to 0.11.6 (#4297) Bumps [prost](https://github.com/tokio-rs/prost) from 0.11.5 to 0.11.6. - [Release notes](https://github.com/tokio-rs/prost/releases) - [Commits](https://github.com/tokio-rs/prost/compare/v0.11.5...v0.11.6) --- updated-dependencies: - dependency-name: prost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35e03264ca9..c511a59344b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3383,9 +3383,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01db6702aa05baa3f57dec92b8eeeeb4cb19e894e73996b32a4093289e54592" +checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" dependencies = [ "bytes", "prost-derive", @@ -3415,9 +3415,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8842bad1a5419bca14eac663ba798f6bc19c413c2fdceb5f3ba3b0932d96720" +checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" dependencies = [ "anyhow", "itertools", diff --git a/Cargo.toml b/Cargo.toml index 8743418e055..3144fc37fe0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ repository = "https://github.com/graphprotocol/graph-node" license = "MIT OR Apache-2.0" [workspace.dependencies] -prost = "0.11.5" +prost = "0.11.6" prost-types = "0.11.5" tonic = { version = "0.8.3", features = ["tls-roots", "gzip"] } tonic-build = { version = "0.8.4", features = ["prost"] } From 07aa19a203616ffc3256db3ea89449e6268248fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jan 2023 16:39:59 +0000 Subject: [PATCH 231/253] build(deps): bump termcolor from 1.1.3 to 1.2.0 (#4294) Bumps [termcolor](https://github.com/BurntSushi/termcolor) from 1.1.3 to 1.2.0. - [Release notes](https://github.com/BurntSushi/termcolor/releases) - [Commits](https://github.com/BurntSushi/termcolor/compare/1.1.3...1.2.0) --- updated-dependencies: - dependency-name: termcolor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- node/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c511a59344b..35f74e3192d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4351,9 +4351,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] diff --git a/node/Cargo.toml b/node/Cargo.toml index b6b8a4aa5d4..b2036af11c1 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -40,7 +40,7 @@ serde = { version = "1.0.126", features = ["derive", "rc"] } serde_regex = "1.1.0" toml = "0.5.7" shellexpand = "2.1.0" -termcolor = "1.1.3" +termcolor = "1.2.0" diesel = "1.4.8" http = "0.2.5" # must be compatible with the version rust-web3 uses prometheus = { version = "0.13.3", features = ["push"] } From 02bff21448bc40714a0ce2a4b8cd01abb2919056 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jan 2023 16:40:19 +0000 Subject: [PATCH 232/253] build(deps): bump atomic_refcell from 0.1.8 to 0.1.9 (#4290) Bumps [atomic_refcell](https://github.com/bholley/atomic_refcell) from 0.1.8 to 0.1.9. - [Release notes](https://github.com/bholley/atomic_refcell/releases) - [Commits](https://github.com/bholley/atomic_refcell/commits) --- updated-dependencies: - dependency-name: atomic_refcell dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- core/Cargo.toml | 2 +- graph/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35f74e3192d..bfb43c0af8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "atomic_refcell" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d" +checksum = "857253367827bd9d0fd973f0ef15506a96e79e41b0ad7aa691203a4e3214f6c8" [[package]] name = "atty" diff --git a/core/Cargo.toml b/core/Cargo.toml index 8936a94ef55..053b36593b9 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -5,7 +5,7 @@ edition.workspace = true [dependencies] async-trait = "0.1.50" -atomic_refcell = "0.1.8" +atomic_refcell = "0.1.9" async-stream = "0.3" bytes = "1.0" futures01 = { package = "futures", version = "0.1.31" } diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 91c766ce4e1..fc1b5974c44 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true anyhow = "1.0" async-trait = "0.1.50" async-stream = "0.3" -atomic_refcell = "0.1.8" +atomic_refcell = "0.1.9" bigdecimal = { version = "0.1.0", features = ["serde"] } bytes = "1.0.1" cid = "0.10.1" diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index 7a2f55b1109..dc54850702c 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -5,7 +5,7 @@ edition.workspace = true [dependencies] async-trait = "0.1.50" -atomic_refcell = "0.1.8" +atomic_refcell = "0.1.9" ethabi = "17.2" futures = "0.1.21" hex = "0.4.3" From 83a3ecc3adbf30af2b1b4e804115402b71af4aae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jan 2023 09:29:17 +0000 Subject: [PATCH 233/253] build(deps): bump bumpalo from 3.7.0 to 3.12.0 (#4306) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfb43c0af8f..8dfa23e00a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,9 +420,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.7.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" From 92670f27d93ed08a6e83805ad644d9004d4d77ee Mon Sep 17 00:00:00 2001 From: Filippo Neysofu Costa Date: Mon, 23 Jan 2023 17:11:25 +0100 Subject: [PATCH 234/253] Env. vars. for forking (#4308) * adding env params for forking opts * adding documentation Co-authored-by: Jeff Wu --- docs/environment-variables.md | 4 ++++ node/src/opt.rs | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 58ca21c71bf..310c83e0a5e 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -215,3 +215,7 @@ those. copying or grafting should take. This limits how long transactions for such long running operations will be, and therefore helps control bloat in other tables. Value is in seconds and defaults to 180s. +- `GRAPH_START_BLOCK`: block hash:block number where the forked subgraph will start indexing at. +- `GRAPH_FORK_BASE`: api url for where the graph node will fork from, use `https://api.thegraph.com/subgraphs/id/` + for the hosted service. +- `GRAPH_DEBUG_FORK`: the IPFS hash id of the subgraph to fork. diff --git a/node/src/opt.rs b/node/src/opt.rs index c40c1c50d2d..321cf47b0cd 100644 --- a/node/src/opt.rs +++ b/node/src/opt.rs @@ -37,6 +37,7 @@ pub struct Opt { #[clap( long, + env = "GRAPH_START_BLOCK", value_name = "BLOCK_HASH:BLOCK_NUMBER", help = "block hash and number that the subgraph passed will start indexing at" )] @@ -217,11 +218,17 @@ pub struct Opt { #[clap( long, value_name = "IPFS_HASH", + env = "GRAPH_DEBUG_FORK", help = "IPFS hash of the subgraph manifest that you want to fork" )] pub debug_fork: Option, - #[clap(long, value_name = "URL", help = "Base URL for forking subgraphs")] + #[clap( + long, + value_name = "URL", + env = "GRAPH_FORK_BASE", + help = "Base URL for forking subgraphs" + )] pub fork_base: Option, } From ad1e98cbedbc096a38b55da4d62180c5277ebbc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:37:59 +0000 Subject: [PATCH 235/253] build(deps): bump prost-types from 0.11.5 to 0.11.6 (#4312) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8dfa23e00a6..291c4a749c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3428,9 +3428,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017f79637768cde62820bc2d4fe0e45daaa027755c323ad077767c6c5f173091" +checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" dependencies = [ "bytes", "prost", diff --git a/Cargo.toml b/Cargo.toml index 3144fc37fe0..74cafca8ba8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ license = "MIT OR Apache-2.0" [workspace.dependencies] prost = "0.11.6" -prost-types = "0.11.5" +prost-types = "0.11.6" tonic = { version = "0.8.3", features = ["tls-roots", "gzip"] } tonic-build = { version = "0.8.4", features = ["prost"] } From 0628b2ca87d2373ce8c7c51e462b3459c0a67c16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:38:53 +0000 Subject: [PATCH 236/253] build(deps): bump git-testament from 0.2.2 to 0.2.4 (#4313) --- Cargo.lock | 4 ++-- store/postgres/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 291c4a749c1..0d3a6fbc64d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1478,9 +1478,9 @@ checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" [[package]] name = "git-testament" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bddca8182c031676362c5ceb276478870044cd714da27a0637aaa143994ad74" +checksum = "986bf57c808270f3a0a0652c3bfce0f5d667aa5f5b465616dc697c7f390834b1" dependencies = [ "git-testament-derive", "no-std-compat", diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index a753d4ca942..b787bc10d5f 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -29,7 +29,7 @@ uuid = { version = "1.1.2", features = ["v4"] } stable-hash_legacy = { version = "0.3.3", package = "stable-hash" } diesel_derives = "1.4.1" anyhow = "1.0.68" -git-testament = "0.2.2" +git-testament = "0.2.4" itertools = "0.10.5" pin-utils = "0.1" hex = "0.4.3" From 04fbd9dfd1880d8d05696460b17346196ad6b782 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Wed, 25 Jan 2023 16:42:18 +0000 Subject: [PATCH 237/253] Add causality region column and implement isolation rules (#4162) * feature(offchain): Add `causality_region` column to entity tables For now this just tracks the tables that need the column and adds the column to the DDL, but still unconditionally inserts 0. Inserting the correct causality region is follow up work. * store: Move `has_causality_region` to manifest, rename to `entities_with_causality_region` * *: Add `causality_region` to EntityKey The tricky part was changing `get_many` to return the entity key. * store: Insert the causality region * store: Read isolation between causality regions It was just necessary to make sure that `find` and `find_many` use the causality region in their where clause. * fix: Fix release build * provider: Make stop idempotent Callers wanted that anyways, and it helps tests. * tests: Refactor file ds test to use events * tests: Test conflict between onchain and offchain * tests: Test conflict between offchain and offchain * test: Fix unit test * tests: Improve tests and comments to address review * fix: Change migration to add column 'if not exists' --- Cargo.lock | 12 ++ chain/substreams/src/trigger.rs | 16 +- core/src/subgraph/provider.rs | 4 +- core/src/subgraph/registrar.rs | 25 ++- core/src/subgraph/runner.rs | 10 +- graph/src/components/store/entity_cache.rs | 28 +--- graph/src/components/store/mod.rs | 47 +++++- graph/src/components/store/traits.rs | 13 +- graph/src/data/graphql/object_or_interface.rs | 4 +- graph/src/data/subgraph/mod.rs | 4 +- graph/src/data/subgraph/schema.rs | 20 ++- graph/src/data/value.rs | 6 + graph/src/data_source/causality_region.rs | 12 +- graph/src/data_source/mod.rs | 14 ++ graph/src/data_source/tests.rs | 22 ++- graph/tests/entity_cache.rs | 38 +++-- graphql/src/introspection/resolver.rs | 2 +- graphql/tests/query.rs | 12 +- runtime/test/src/test.rs | 5 +- runtime/wasm/src/host_exports.rs | 15 +- store/postgres/Cargo.toml | 1 + store/postgres/examples/layout.rs | 3 +- .../down.sql | 1 + .../up.sql | 1 + store/postgres/src/block_range.rs | 3 + store/postgres/src/catalog.rs | 34 +++-- store/postgres/src/deployment.rs | 31 +++- store/postgres/src/deployment_store.rs | 21 ++- store/postgres/src/detail.rs | 3 + store/postgres/src/relational.rs | 59 ++++--- store/postgres/src/relational/ddl.rs | 78 ++++++---- store/postgres/src/relational/ddl_tests.rs | 56 +++++-- store/postgres/src/relational/query_tests.rs | 5 +- store/postgres/src/relational_queries.rs | 83 ++++++++-- store/postgres/src/writable.rs | 81 +++++----- store/postgres/tests/graft.rs | 10 +- store/postgres/tests/relational.rs | 49 ++++-- store/postgres/tests/relational_bytes.rs | 76 ++++++--- store/test-store/src/store.rs | 2 + tests/Cargo.toml | 1 + .../file-data-sources/abis/Contract.abi | 16 +- .../file-data-sources/src/mapping.ts | 60 +++++++- .../file-data-sources/subgraph.yaml | 5 +- tests/src/fixture.rs | 93 ++++++++++- tests/src/fixture/ethereum.rs | 56 ++++++- tests/tests/runner.rs | 144 +++++++++++------- 46 files changed, 925 insertions(+), 356 deletions(-) create mode 100644 store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/down.sql create mode 100644 store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/up.sql diff --git a/Cargo.lock b/Cargo.lock index 0d3a6fbc64d..17be6b764e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-recursion" version = "1.0.0" @@ -1944,6 +1954,7 @@ dependencies = [ "pin-utils", "postgres", "postgres-openssl", + "pretty_assertions", "rand", "serde", "stable-hash 0.3.3", @@ -1956,6 +1967,7 @@ name = "graph-tests" version = "0.29.0" dependencies = [ "anyhow", + "assert-json-diff", "async-stream", "bollard", "cid", diff --git a/chain/substreams/src/trigger.rs b/chain/substreams/src/trigger.rs index d18b5bbe6b4..237f9e09793 100644 --- a/chain/substreams/src/trigger.rs +++ b/chain/substreams/src/trigger.rs @@ -4,11 +4,11 @@ use anyhow::Error; use graph::{ blockchain::{self, block_stream::BlockWithTriggers, BlockPtr, EmptyNodeCapabilities}, components::{ - store::{DeploymentLocator, EntityKey, SubgraphFork}, + store::{DeploymentLocator, EntityKey, EntityType, SubgraphFork}, subgraph::{MappingError, ProofOfIndexingEvent, SharedProofOfIndexing}, }, data::store::scalar::Bytes, - data_source, + data_source::{self, CausalityRegion}, prelude::{ anyhow, async_trait, BigDecimal, BigInt, BlockHash, BlockNumber, BlockState, Entity, RuntimeHostBuilder, Value, @@ -183,7 +183,11 @@ where Operation::Create | Operation::Update => { let entity_type: &str = &entity_change.entity; let entity_id: String = entity_change.id.clone(); - let key = EntityKey::data(entity_type.to_string(), entity_id.clone()); + let key = EntityKey { + entity_type: EntityType::new(entity_type.to_string()), + entity_id: entity_id.clone().into(), + causality_region: CausalityRegion::ONCHAIN, // Substreams don't currently support offchain data + }; let mut data: HashMap = HashMap::from_iter(vec![]); for field in entity_change.fields.iter() { @@ -214,7 +218,11 @@ where Operation::Delete => { let entity_type: &str = &entity_change.entity; let entity_id: String = entity_change.id.clone(); - let key = EntityKey::data(entity_type.to_string(), entity_id.clone()); + let key = EntityKey { + entity_type: EntityType::new(entity_type.to_string()), + entity_id: entity_id.clone().into(), + causality_region: CausalityRegion::ONCHAIN, // Substreams don't currently support offchain data + }; state.entity_cache.remove(key); diff --git a/core/src/subgraph/provider.rs b/core/src/subgraph/provider.rs index 11b325af3f9..4d3a0cab51d 100644 --- a/core/src/subgraph/provider.rs +++ b/core/src/subgraph/provider.rs @@ -82,9 +82,7 @@ impl SubgraphAssignmentProviderTrait for SubgraphAss { // Shut down subgraph processing self.instance_manager.stop_subgraph(deployment).await; - Ok(()) - } else { - Err(SubgraphAssignmentProviderError::NotRunning(deployment)) } + Ok(()) } } diff --git a/core/src/subgraph/registrar.rs b/core/src/subgraph/registrar.rs index 8e3bddeff26..3ca52d98bef 100644 --- a/core/src/subgraph/registrar.rs +++ b/core/src/subgraph/registrar.rs @@ -88,8 +88,7 @@ where // - The event stream sees a Remove event for subgraph B, but the table query finds that // subgraph B has already been removed. // The `handle_assignment_events` function handles these cases by ignoring AlreadyRunning - // (on subgraph start) or NotRunning (on subgraph stop) error types, which makes the - // operations idempotent. + // (on subgraph start) which makes the operations idempotent. Subgraph stop is already idempotent. // Start event stream let assignment_event_stream = self.assignment_events(); @@ -455,7 +454,6 @@ async fn handle_assignment_event( node_id: _, } => match provider.stop(deployment).await { Ok(()) => Ok(()), - Err(SubgraphAssignmentProviderError::NotRunning(_)) => Ok(()), Err(e) => Err(CancelableError::Error(e)), }, } @@ -620,11 +618,30 @@ async fn create_subgraph_version( "block" => format!("{:?}", base_block.as_ref().map(|(_,ptr)| ptr.number)) ); + // Entity types that may be touched by offchain data sources need a causality region column. + let needs_causality_region = manifest + .data_sources + .iter() + .filter_map(|ds| ds.as_offchain()) + .map(|ds| ds.mapping.entities.iter()) + .chain( + manifest + .templates + .iter() + .filter_map(|ds| ds.as_offchain()) + .map(|ds| ds.mapping.entities.iter()), + ) + .flatten() + .cloned() + .collect(); + // Apply the subgraph versioning and deployment operations, // creating a new subgraph deployment if one doesn't exist. let deployment = DeploymentCreate::new(raw_string, &manifest, start_block) .graft(base_block) - .debug(debug_fork); + .debug(debug_fork) + .entities_with_causality_region(needs_causality_region); + deployment_store .create_subgraph_deployment( name, diff --git a/core/src/subgraph/runner.rs b/core/src/subgraph/runner.rs index af5a338dcb2..515fa53c066 100644 --- a/core/src/subgraph/runner.rs +++ b/core/src/subgraph/runner.rs @@ -17,7 +17,7 @@ use graph::data::subgraph::{ SubgraphFeature, }; use graph::data_source::{ - offchain, DataSource, DataSourceCreationError, DataSourceTemplate, TriggerData, + offchain, CausalityRegion, DataSource, DataSourceCreationError, DataSourceTemplate, TriggerData, }; use graph::env::EnvVars; use graph::prelude::*; @@ -653,6 +653,7 @@ where let mut block_state = BlockState::::new(EmptyStore::new(schema), LfuCache::new()); // PoI ignores offchain events. + // See also: poi-ignores-offchain let proof_of_indexing = None; let causality_region = ""; @@ -998,7 +999,14 @@ async fn update_proof_of_indexing( // Create the special POI entity key specific to this causality_region let entity_key = EntityKey { entity_type: POI_OBJECT.to_owned(), + + // There are two things called causality regions here, one is the causality region for + // the poi which is a string and the PoI entity id. The other is the data source + // causality region to which the PoI belongs as an entity. Currently offchain events do + // not affect PoI so it is assumed to be `ONCHAIN`. + // See also: poi-ignores-offchain entity_id: causality_region.into(), + causality_region: CausalityRegion::ONCHAIN, }; // Grab the current digest attribute on this entity diff --git a/graph/src/components/store/entity_cache.rs b/graph/src/components/store/entity_cache.rs index 252bad33073..50fc357a890 100644 --- a/graph/src/components/store/entity_cache.rs +++ b/graph/src/components/store/entity_cache.rs @@ -1,11 +1,9 @@ use anyhow::anyhow; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::fmt::{self, Debug}; use std::sync::Arc; -use crate::components::store::{ - self as s, Entity, EntityKey, EntityOp, EntityOperation, EntityType, -}; +use crate::components::store::{self as s, Entity, EntityKey, EntityOp, EntityOperation}; use crate::prelude::{Schema, ENV_VARS}; use crate::util::lfu_cache::LfuCache; @@ -102,6 +100,10 @@ impl EntityCache { // Get the current entity, apply any updates from `updates`, then // from `handler_updates`. let mut entity = self.current.get_entity(&*self.store, eref)?; + + // Always test the cache consistency in debug mode. + debug_assert!(entity == self.store.get(&eref).unwrap()); + if let Some(op) = self.updates.get(eref).cloned() { entity = op.apply_to(entity) } @@ -233,22 +235,8 @@ impl EntityCache { // violation in the database, ensuring correctness let missing = missing.filter(|key| !self.schema.is_immutable(&key.entity_type)); - let mut missing_by_type: BTreeMap<&EntityType, Vec<&str>> = BTreeMap::new(); - for key in missing { - missing_by_type - .entry(&key.entity_type) - .or_default() - .push(&key.entity_id); - } - - for (entity_type, entities) in self.store.get_many(missing_by_type)? { - for entity in entities { - let key = EntityKey { - entity_type: entity_type.clone(), - entity_id: entity.id().unwrap().into(), - }; - self.current.insert(key, Some(entity)); - } + for (entity_key, entity) in self.store.get_many(missing.cloned().collect())? { + self.current.insert(entity_key, Some(entity)); } let mut mods = Vec::new(); diff --git a/graph/src/components/store/mod.rs b/graph/src/components/store/mod.rs index c20b58cc295..d03c0c26e91 100644 --- a/graph/src/components/store/mod.rs +++ b/graph/src/components/store/mod.rs @@ -4,6 +4,7 @@ mod traits; pub use entity_cache::{EntityCache, ModificationsAndCache}; +use diesel::types::{FromSql, ToSql}; pub use err::StoreError; use itertools::Itertools; pub use traits::*; @@ -12,13 +13,14 @@ use futures::stream::poll_fn; use futures::{Async, Poll, Stream}; use graphql_parser::schema as s; use serde::{Deserialize, Serialize}; +use std::borrow::Borrow; use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet, HashSet}; -use std::fmt; use std::fmt::Display; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Duration; +use std::{fmt, io}; use crate::blockchain::Block; use crate::data::store::scalar::Bytes; @@ -71,6 +73,12 @@ impl<'a> From<&s::InterfaceType<'a, String>> for EntityType { } } +impl Borrow for EntityType { + fn borrow(&self) -> &str { + &self.0 + } +} + // This conversion should only be used in tests since it makes it too // easy to convert random strings into entity types #[cfg(debug_assertions)] @@ -82,6 +90,22 @@ impl From<&str> for EntityType { impl CheapClone for EntityType {} +impl FromSql for EntityType { + fn from_sql(bytes: Option<&[u8]>) -> diesel::deserialize::Result { + let s = >::from_sql(bytes)?; + Ok(EntityType::new(s)) + } +} + +impl ToSql for EntityType { + fn to_sql( + &self, + out: &mut diesel::serialize::Output, + ) -> diesel::serialize::Result { + >::to_sql(self.0.as_str(), out) + } +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct EntityFilterDerivative(bool); @@ -104,13 +128,23 @@ pub struct EntityKey { /// ID of the individual entity. pub entity_id: Word, + + /// This is the causality region of the data source that created the entity. + /// + /// In the case of an entity lookup, this is the causality region of the data source that is + /// doing the lookup. So if the entity exists but was created on a different causality region, + /// the lookup will return empty. + pub causality_region: CausalityRegion, } impl EntityKey { - pub fn data(entity_type: String, entity_id: String) -> Self { + // For use in tests only + #[cfg(debug_assertions)] + pub fn data(entity_type: impl Into, entity_id: impl Into) -> Self { Self { - entity_type: EntityType::new(entity_type), - entity_id: entity_id.into(), + entity_type: EntityType::new(entity_type.into()), + entity_id: entity_id.into().into(), + causality_region: CausalityRegion::ONCHAIN, } } } @@ -1071,10 +1105,7 @@ impl ReadStore for EmptyStore { Ok(None) } - fn get_many( - &self, - _ids_for_type: BTreeMap<&EntityType, Vec<&str>>, - ) -> Result>, StoreError> { + fn get_many(&self, _: BTreeSet) -> Result, StoreError> { Ok(BTreeMap::new()) } diff --git a/graph/src/components/store/traits.rs b/graph/src/components/store/traits.rs index 24d3918907e..6a6ef2bc79d 100644 --- a/graph/src/components/store/traits.rs +++ b/graph/src/components/store/traits.rs @@ -171,12 +171,11 @@ pub trait ReadStore: Send + Sync + 'static { /// Looks up an entity using the given store key at the latest block. fn get(&self, key: &EntityKey) -> Result, StoreError>; - /// Look up multiple entities as of the latest block. Returns a map of - /// entities by type. + /// Look up multiple entities as of the latest block. fn get_many( &self, - ids_for_type: BTreeMap<&EntityType, Vec<&str>>, - ) -> Result>, StoreError>; + keys: BTreeSet, + ) -> Result, StoreError>; fn input_schema(&self) -> Arc; } @@ -189,9 +188,9 @@ impl ReadStore for Arc { fn get_many( &self, - ids_for_type: BTreeMap<&EntityType, Vec<&str>>, - ) -> Result>, StoreError> { - (**self).get_many(ids_for_type) + keys: BTreeSet, + ) -> Result, StoreError> { + (**self).get_many(keys) } fn input_schema(&self) -> Arc { diff --git a/graph/src/data/graphql/object_or_interface.rs b/graph/src/data/graphql/object_or_interface.rs index 48053555e76..7764769a1a0 100644 --- a/graph/src/data/graphql/object_or_interface.rs +++ b/graph/src/data/graphql/object_or_interface.rs @@ -117,7 +117,7 @@ impl<'a> ObjectOrInterface<'a> { ObjectOrInterface::Object(object) => Some(vec![object]), ObjectOrInterface::Interface(interface) => schema .types_for_interface() - .get(&interface.into()) + .get(interface.name.as_str()) .map(|object_types| object_types.iter().collect()), } } @@ -131,7 +131,7 @@ impl<'a> ObjectOrInterface<'a> { ) -> bool { match self { ObjectOrInterface::Object(o) => o.name == typename, - ObjectOrInterface::Interface(i) => types_for_interface[&i.into()] + ObjectOrInterface::Interface(i) => types_for_interface[i.name.as_str()] .iter() .any(|o| o.name == typename), } diff --git a/graph/src/data/subgraph/mod.rs b/graph/src/data/subgraph/mod.rs index e21423c18b6..114c2f66936 100644 --- a/graph/src/data/subgraph/mod.rs +++ b/graph/src/data/subgraph/mod.rs @@ -28,7 +28,7 @@ use crate::{ blockchain::{BlockPtr, Blockchain, DataSource as _}, components::{ link_resolver::LinkResolver, - store::{DeploymentLocator, StoreError, SubgraphStore}, + store::{StoreError, SubgraphStore}, }, data::{ graphql::TryFromValue, @@ -304,8 +304,6 @@ pub enum SubgraphAssignmentProviderError { /// Occurs when attempting to remove a subgraph that's not hosted. #[error("Subgraph with ID {0} already running")] AlreadyRunning(DeploymentHash), - #[error("Subgraph with ID {0} is not running")] - NotRunning(DeploymentLocator), #[error("Subgraph provider error: {0}")] Unknown(#[from] anyhow::Error), } diff --git a/graph/src/data/subgraph/schema.rs b/graph/src/data/subgraph/schema.rs index eda88f9f522..11f4ed6cfea 100644 --- a/graph/src/data/subgraph/schema.rs +++ b/graph/src/data/subgraph/schema.rs @@ -5,6 +5,7 @@ use hex; use lazy_static::lazy_static; use rand::rngs::OsRng; use rand::Rng; +use std::collections::BTreeSet; use std::str::FromStr; use std::{fmt, fmt::Display}; @@ -116,7 +117,7 @@ impl DeploymentCreate { start_block: Option, ) -> Self { Self { - manifest: SubgraphManifestEntity::new(raw_manifest, source_manifest), + manifest: SubgraphManifestEntity::new(raw_manifest, source_manifest, Vec::new()), start_block: start_block.cheap_clone(), graft_base: None, graft_block: None, @@ -136,6 +137,15 @@ impl DeploymentCreate { self.debug_fork = fork; self } + + pub fn entities_with_causality_region( + mut self, + entities_with_causality_region: BTreeSet, + ) -> Self { + self.manifest.entities_with_causality_region = + entities_with_causality_region.into_iter().collect(); + self + } } /// The representation of a subgraph deployment when reading an existing @@ -169,10 +179,15 @@ pub struct SubgraphManifestEntity { pub features: Vec, pub schema: String, pub raw_yaml: Option, + pub entities_with_causality_region: Vec, } impl SubgraphManifestEntity { - pub fn new(raw_yaml: String, manifest: &super::SubgraphManifest) -> Self { + pub fn new( + raw_yaml: String, + manifest: &super::SubgraphManifest, + entities_with_causality_region: Vec, + ) -> Self { Self { spec_version: manifest.spec_version.to_string(), description: manifest.description.clone(), @@ -180,6 +195,7 @@ impl SubgraphManifestEntity { features: manifest.features.iter().map(|f| f.to_string()).collect(), schema: manifest.schema.document.clone().to_string(), raw_yaml: Some(raw_yaml), + entities_with_causality_region, } } diff --git a/graph/src/data/value.rs b/graph/src/data/value.rs index f3b94a2c9fa..ddb2d0a7134 100644 --- a/graph/src/data/value.rs +++ b/graph/src/data/value.rs @@ -43,6 +43,12 @@ impl From for Word { } } +impl From for String { + fn from(w: Word) -> Self { + w.0.into() + } +} + impl Serialize for Word { fn serialize(&self, serializer: S) -> Result where diff --git a/graph/src/data_source/causality_region.rs b/graph/src/data_source/causality_region.rs index 2c297a5c930..808538b6027 100644 --- a/graph/src/data_source/causality_region.rs +++ b/graph/src/data_source/causality_region.rs @@ -8,6 +8,8 @@ use diesel::{ use std::fmt; use std::io; +use crate::components::subgraph::Entity; + /// The causality region of a data source. All onchain data sources share the same causality region, /// but each offchain data source is assigned its own. This isolates offchain data sources from /// onchain and from each other. @@ -19,7 +21,7 @@ use std::io; /// This necessary for determinism because offchain data sources don't have a deterministic order of /// execution, for example an IPFS file may become available at any point in time. The isolation /// rules make the indexing result reproducible, given a set of available files. -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromSqlRow)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, FromSqlRow, Hash, PartialOrd, Ord)] pub struct CausalityRegion(i32); impl fmt::Display for CausalityRegion { @@ -47,6 +49,14 @@ impl CausalityRegion { pub const fn next(self) -> Self { CausalityRegion(self.0 + 1) } + + pub fn from_entity(entity: &Entity) -> Self { + entity + .get("causality_region") + .and_then(|v| v.as_int()) + .map(CausalityRegion) + .unwrap_or(CausalityRegion::ONCHAIN) + } } /// A subgraph will assign causality regions to offchain data sources from a sequence. diff --git a/graph/src/data_source/mod.rs b/graph/src/data_source/mod.rs index a2b5d5b97c2..aba6498682b 100644 --- a/graph/src/data_source/mod.rs +++ b/graph/src/data_source/mod.rs @@ -206,6 +206,13 @@ impl DataSource { Self::Offchain(_) => vec![], } } + + pub fn causality_region(&self) -> CausalityRegion { + match self { + Self::Onchain(_) => CausalityRegion::ONCHAIN, + Self::Offchain(ds) => ds.causality_region, + } + } } #[derive(Debug)] @@ -250,6 +257,13 @@ impl DataSourceTemplate { } } + pub fn as_offchain(&self) -> Option<&offchain::DataSourceTemplate> { + match self { + Self::Onchain(_) => None, + Self::Offchain(t) => Some(&t), + } + } + pub fn into_onchain(self) -> Option { match self { Self::Onchain(ds) => Some(ds), diff --git a/graph/src/data_source/tests.rs b/graph/src/data_source/tests.rs index 0312fd3dbd6..30421fca84f 100644 --- a/graph/src/data_source/tests.rs +++ b/graph/src/data_source/tests.rs @@ -1,6 +1,11 @@ use cid::Cid; -use crate::{components::subgraph::Entity, ipfs_client::CidFile, prelude::Link}; +use crate::{ + blockchain::mock::{MockBlockchain, MockDataSource}, + components::subgraph::Entity, + ipfs_client::CidFile, + prelude::Link, +}; use super::{ offchain::{Mapping, Source}, @@ -45,6 +50,21 @@ fn offchain_mark_processed_error() { x.mark_processed_at(-1) } +#[test] +fn data_source_helpers() { + let offchain = new_datasource(); + let offchain_ds = DataSource::::Offchain(offchain.clone()); + assert!(offchain_ds.causality_region() == offchain.causality_region); + assert!(offchain_ds + .as_offchain() + .unwrap() + .is_duplicate_of(&offchain)); + + let onchain = DataSource::::Onchain(MockDataSource); + assert!(onchain.causality_region() == CausalityRegion::ONCHAIN); + assert!(onchain.as_offchain().is_none()); +} + fn new_datasource() -> offchain::DataSource { offchain::DataSource::new( "theKind".into(), diff --git a/graph/tests/entity_cache.rs b/graph/tests/entity_cache.rs index 5cff8d0a251..ffe2388d058 100644 --- a/graph/tests/entity_cache.rs +++ b/graph/tests/entity_cache.rs @@ -6,7 +6,7 @@ use graph::data_source::CausalityRegion; use graph::prelude::{Schema, StopwatchMetrics, StoreError, UnfailOutcome}; use lazy_static::lazy_static; use slog::Logger; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::sync::Arc; use graph::components::store::{ @@ -14,7 +14,7 @@ use graph::components::store::{ }; use graph::{ components::store::{DeploymentId, DeploymentLocator}, - prelude::{anyhow, DeploymentHash, Entity, EntityCache, EntityModification, Value}, + prelude::{DeploymentHash, Entity, EntityCache, EntityModification, Value}, }; lazy_static! { @@ -38,33 +38,24 @@ lazy_static! { } struct MockStore { - get_many_res: BTreeMap>, + get_many_res: BTreeMap, } impl MockStore { - fn new(get_many_res: BTreeMap>) -> Self { + fn new(get_many_res: BTreeMap) -> Self { Self { get_many_res } } } impl ReadStore for MockStore { fn get(&self, key: &EntityKey) -> Result, StoreError> { - match self.get_many_res.get(&key.entity_type) { - Some(entities) => Ok(entities - .iter() - .find(|entity| entity.id().ok().as_deref() == Some(key.entity_id.as_str())) - .cloned()), - None => Err(StoreError::Unknown(anyhow!( - "nothing for type {}", - key.entity_type - ))), - } + Ok(self.get_many_res.get(&key).cloned()) } fn get_many( &self, - _ids_for_type: BTreeMap<&EntityType, Vec<&str>>, - ) -> Result>, StoreError> { + _keys: BTreeSet, + ) -> Result, StoreError> { Ok(self.get_many_res.clone()) } @@ -170,6 +161,7 @@ fn make_band(id: &'static str, data: Vec<(&str, Value)>) -> (EntityKey, Entity) EntityKey { entity_type: EntityType::new("Band".to_string()), entity_id: id.into(), + causality_region: CausalityRegion::ONCHAIN, }, Entity::from(data), ) @@ -227,12 +219,16 @@ fn insert_modifications() { ); } -fn entity_version_map( - entity_type: &str, - entities: Vec, -) -> BTreeMap> { +fn entity_version_map(entity_type: &str, entities: Vec) -> BTreeMap { let mut map = BTreeMap::new(); - map.insert(EntityType::from(entity_type), entities); + for entity in entities { + let key = EntityKey { + entity_type: EntityType::new(entity_type.to_string()), + entity_id: entity.id().unwrap().into(), + causality_region: CausalityRegion::ONCHAIN, + }; + map.insert(key, entity); + } map } diff --git a/graphql/src/introspection/resolver.rs b/graphql/src/introspection/resolver.rs index ed95e0066b9..f71ee350797 100644 --- a/graphql/src/introspection/resolver.rs +++ b/graphql/src/introspection/resolver.rs @@ -135,7 +135,7 @@ fn interface_type_object( description: interface_type.description.clone(), fields: field_objects(schema, type_objects, &interface_type.fields), - possibleTypes: schema.types_for_interface()[&interface_type.into()] + possibleTypes: schema.types_for_interface()[interface_type.name.as_str()] .iter() .map(|object_type| r::Value::String(object_type.name.to_owned())) .collect::>(), diff --git a/graphql/tests/query.rs b/graphql/tests/query.rs index a6ee479a88c..e906f06ed7a 100644 --- a/graphql/tests/query.rs +++ b/graphql/tests/query.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate pretty_assertions; -use graph::components::store::{EntityKey, EntityType}; +use graph::components::store::EntityKey; use graph::data::subgraph::schema::DeploymentCreate; use graph::entity; use graph::prelude::SubscriptionResult; @@ -320,12 +320,10 @@ async fn insert_test_entities( async fn insert_at(entities: Vec, deployment: &DeploymentLocator, block_ptr: BlockPtr) { let insert_ops = entities.into_iter().map(|data| EntityOperation::Set { - key: EntityKey { - entity_type: EntityType::new( - data.get("__typename").unwrap().clone().as_string().unwrap(), - ), - entity_id: data.get("id").unwrap().clone().as_string().unwrap().into(), - }, + key: EntityKey::data( + data.get("__typename").unwrap().clone().as_string().unwrap(), + data.get("id").unwrap().clone().as_string().unwrap(), + ), data, }); diff --git a/runtime/test/src/test.rs b/runtime/test/src/test.rs index cad61b5c897..939daf0e074 100644 --- a/runtime/test/src/test.rs +++ b/runtime/test/src/test.rs @@ -423,10 +423,7 @@ fn make_thing(id: &str, value: &str) -> (String, EntityModification) { data.set("id", id); data.set("value", value); data.set("extra", USER_DATA); - let key = EntityKey { - entity_type: EntityType::new("Thing".to_string()), - entity_id: id.into(), - }; + let key = EntityKey::data("Thing".to_string(), id); ( format!("{{ \"id\": \"{}\", \"value\": \"{}\"}}", id, value), EntityModification::Insert { key, data }, diff --git a/runtime/wasm/src/host_exports.rs b/runtime/wasm/src/host_exports.rs index d6eaf0d0f24..8c64233c975 100644 --- a/runtime/wasm/src/host_exports.rs +++ b/runtime/wasm/src/host_exports.rs @@ -15,7 +15,7 @@ use graph::components::subgraph::{ PoICausalityRegion, ProofOfIndexingEvent, SharedProofOfIndexing, }; use graph::data::store; -use graph::data_source::{DataSource, DataSourceTemplate, EntityTypeAccess}; +use graph::data_source::{CausalityRegion, DataSource, DataSourceTemplate, EntityTypeAccess}; use graph::ensure; use graph::prelude::ethabi::param_type::Reader; use graph::prelude::ethabi::{decode, encode, Token}; @@ -64,12 +64,13 @@ pub struct HostExports { subgraph_network: String, data_source_context: Arc>, entity_type_access: EntityTypeAccess, + data_source_causality_region: CausalityRegion, /// Some data sources have indeterminism or different notions of time. These /// need to be each be stored separately to separate causality between them, /// and merge the results later. Right now, this is just the ethereum /// networks but will be expanded for ipfs and the availability chain. - causality_region: String, + poi_causality_region: String, templates: Arc>>, pub(crate) link_resolver: Arc, ens_lookup: Arc, @@ -91,7 +92,8 @@ impl HostExports { data_source_address: data_source.address().unwrap_or_default(), data_source_context: data_source.context().cheap_clone(), entity_type_access: data_source.entities(), - causality_region: PoICausalityRegion::from_network(&subgraph_network), + data_source_causality_region: data_source.causality_region(), + poi_causality_region: PoICausalityRegion::from_network(&subgraph_network), subgraph_network, templates, link_resolver, @@ -165,7 +167,7 @@ impl HostExports { id: &entity_id, data: &data, }, - &self.causality_region, + &self.poi_causality_region, logger, ); poi_section.end(); @@ -173,6 +175,7 @@ impl HostExports { let key = EntityKey { entity_type: EntityType::new(entity_type), entity_id: entity_id.into(), + causality_region: self.data_source_causality_region, }; self.check_entity_type_access(&key.entity_type)?; @@ -199,12 +202,13 @@ impl HostExports { entity_type: &entity_type, id: &entity_id, }, - &self.causality_region, + &self.poi_causality_region, logger, ); let key = EntityKey { entity_type: EntityType::new(entity_type), entity_id: entity_id.into(), + causality_region: self.data_source_causality_region, }; self.check_entity_type_access(&key.entity_type)?; @@ -225,6 +229,7 @@ impl HostExports { let store_key = EntityKey { entity_type: EntityType::new(entity_type), entity_id: entity_id.into(), + causality_region: self.data_source_causality_region, }; self.check_entity_type_access(&store_key.entity_type)?; diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index b787bc10d5f..e2f50668759 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -33,6 +33,7 @@ git-testament = "0.2.4" itertools = "0.10.5" pin-utils = "0.1" hex = "0.4.3" +pretty_assertions = "1.3.0" [dev-dependencies] futures = "0.3" diff --git a/store/postgres/examples/layout.rs b/store/postgres/examples/layout.rs index 1f73934adda..1bf156d5f21 100644 --- a/store/postgres/examples/layout.rs +++ b/store/postgres/examples/layout.rs @@ -2,6 +2,7 @@ extern crate clap; extern crate graph_store_postgres; use clap::{arg, Command}; +use std::collections::BTreeSet; use std::process::exit; use std::{fs, sync::Arc}; @@ -145,7 +146,7 @@ pub fn main() { ); let site = Arc::new(make_dummy_site(subgraph, namespace, "anet".to_string())); let catalog = ensure( - Catalog::for_tests(site.clone()), + Catalog::for_tests(site.clone(), BTreeSet::new()), "Failed to construct catalog", ); let layout = ensure( diff --git a/store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/down.sql b/store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/down.sql new file mode 100644 index 00000000000..4775fa58a3b --- /dev/null +++ b/store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/down.sql @@ -0,0 +1 @@ +alter table subgraphs.subgraph_manifest drop column entities_with_causality_region; diff --git a/store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/up.sql b/store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/up.sql new file mode 100644 index 00000000000..d82d5ad2628 --- /dev/null +++ b/store/postgres/migrations/2022-11-10-185105_add_has_causality_region_column/up.sql @@ -0,0 +1 @@ +alter table subgraphs.subgraph_manifest add column if not exists entities_with_causality_region text[] not null default array[]::text[]; diff --git a/store/postgres/src/block_range.rs b/store/postgres/src/block_range.rs index aafaeca29ef..a8d85e4c21c 100644 --- a/store/postgres/src/block_range.rs +++ b/store/postgres/src/block_range.rs @@ -15,6 +15,9 @@ use crate::relational::{SqlName, Table}; /// entities pub(crate) const BLOCK_RANGE_COLUMN: &str = "block_range"; +/// The name of the column that stores the causality region of an entity. +pub(crate) const CAUSALITY_REGION_COLUMN: &str = "causality_region"; + /// The SQL clause we use to check that an entity version is current; /// that version has an unbounded block range, but checking for /// `upper_inf(block_range)` is slow and can't use the exclusion diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 77d8bd25be2..6228ce66842 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -6,9 +6,10 @@ use diesel::{ sql_types::{Array, Double, Nullable, Text}, ExpressionMethods, QueryDsl, }; +use graph::components::store::EntityType; use graph::components::store::VersionStats; use itertools::Itertools; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fmt::Write; use std::iter::FromIterator; use std::sync::Arc; @@ -115,13 +116,6 @@ lazy_static! { SqlName::verbatim("__diesel_schema_migrations".to_string()); } -// In debug builds (for testing etc.) create exclusion constraints, in -// release builds for production, skip them -#[cfg(debug_assertions)] -const CREATE_EXCLUSION_CONSTRAINT: bool = true; -#[cfg(not(debug_assertions))] -const CREATE_EXCLUSION_CONSTRAINT: bool = false; - pub struct Locale { collate: String, ctype: String, @@ -177,11 +171,15 @@ impl Locale { pub struct Catalog { pub site: Arc, text_columns: HashMap>, + pub use_poi: bool, /// Whether `bytea` columns are indexed with just a prefix (`true`) or /// in their entirety. This influences both DDL generation and how /// queries are generated pub use_bytea_prefix: bool, + + /// Set of tables which have an explicit causality region column. + pub(crate) entities_with_causality_region: BTreeSet, } impl Catalog { @@ -190,6 +188,7 @@ impl Catalog { conn: &PgConnection, site: Arc, use_bytea_prefix: bool, + entities_with_causality_region: Vec, ) -> Result { let text_columns = get_text_columns(conn, &site.namespace)?; let use_poi = supports_proof_of_indexing(conn, &site.namespace)?; @@ -198,11 +197,15 @@ impl Catalog { text_columns, use_poi, use_bytea_prefix, + entities_with_causality_region: entities_with_causality_region.into_iter().collect(), }) } /// Return a new catalog suitable for creating a new subgraph - pub fn for_creation(site: Arc) -> Self { + pub fn for_creation( + site: Arc, + entities_with_causality_region: BTreeSet, + ) -> Self { Catalog { site, text_columns: HashMap::default(), @@ -211,18 +214,23 @@ impl Catalog { // DDL generation creates indexes for prefixes of bytes columns // see: attr-bytea-prefix use_bytea_prefix: true, + entities_with_causality_region, } } /// Make a catalog as if the given `schema` did not exist in the database /// yet. This function should only be used in situations where a database /// connection is definitely not available, such as in unit tests - pub fn for_tests(site: Arc) -> Result { + pub fn for_tests( + site: Arc, + entities_with_causality_region: BTreeSet, + ) -> Result { Ok(Catalog { site, text_columns: HashMap::default(), use_poi: false, use_bytea_prefix: true, + entities_with_causality_region, }) } @@ -234,12 +242,6 @@ impl Catalog { .map(|cols| cols.contains(column.as_str())) .unwrap_or(false) } - - /// Whether to create exclusion indexes; if false, create gist indexes - /// w/o an exclusion constraint - pub fn create_exclusion_constraint() -> bool { - CREATE_EXCLUSION_CONSTRAINT - } } fn get_text_columns( diff --git a/store/postgres/src/deployment.rs b/store/postgres/src/deployment.rs index fdc4e6df617..32b7b68c41f 100644 --- a/store/postgres/src/deployment.rs +++ b/store/postgres/src/deployment.rs @@ -13,11 +13,14 @@ use diesel::{ sql_query, sql_types::{Nullable, Text}, }; -use graph::prelude::{ - anyhow, bigdecimal::ToPrimitive, hex, web3::types::H256, BigDecimal, BlockNumber, BlockPtr, - DeploymentHash, DeploymentState, Schema, StoreError, -}; use graph::{blockchain::block_stream::FirehoseCursor, data::subgraph::schema::SubgraphError}; +use graph::{ + components::store::EntityType, + prelude::{ + anyhow, bigdecimal::ToPrimitive, hex, web3::types::H256, BigDecimal, BlockNumber, BlockPtr, + DeploymentHash, DeploymentState, Schema, StoreError, + }, +}; use graph::{ data::subgraph::{ schema::{DeploymentCreate, SubgraphManifestEntity}, @@ -114,6 +117,10 @@ table! { start_block_number -> Nullable, start_block_hash -> Nullable, raw_yaml -> Nullable, + + // Entity types that have a `causality_region` column. + // Names stored as present in the schema, not in snake case. + entities_with_causality_region -> Array, } } @@ -769,6 +776,19 @@ pub(crate) fn health(conn: &PgConnection, id: DeploymentId) -> Result Result, StoreError> { + use subgraph_manifest as sm; + + sm::table + .filter(sm::id.eq(id)) + .select(sm::entities_with_causality_region) + .get_result(conn) + .map_err(|e| e.into()) +} + /// Reverts the errors and updates the subgraph health if necessary. pub(crate) fn revert_subgraph_errors( conn: &PgConnection, @@ -926,6 +946,7 @@ pub fn create_deployment( features, schema, raw_yaml, + entities_with_causality_region, }, start_block, graft_base, @@ -933,6 +954,7 @@ pub fn create_deployment( debug_fork, } = deployment; let earliest_block_number = start_block.as_ref().map(|ptr| ptr.number).unwrap_or(0); + let entities_with_causality_region = Vec::from_iter(entities_with_causality_region.into_iter()); let deployment_values = ( d::id.eq(site.id), @@ -968,6 +990,7 @@ pub fn create_deployment( m::start_block_hash.eq(b(&start_block)), m::start_block_number.eq(start_block.as_ref().map(|ptr| ptr.number)), m::raw_yaml.eq(raw_yaml), + m::entities_with_causality_region.eq(entities_with_causality_region), ); if exists && replace { diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index a1fc1f95e1f..ec73672c7ad 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -175,6 +175,8 @@ impl DeploymentStore { let exists = deployment::exists(&conn, &site)?; // Create (or update) the metadata. Update only happens in tests + let entities_with_causality_region = + deployment.manifest.entities_with_causality_region.clone(); if replace || !exists { deployment::create_deployment(&conn, &site, deployment, exists, replace)?; }; @@ -184,7 +186,12 @@ impl DeploymentStore { let query = format!("create schema {}", &site.namespace); conn.batch_execute(&query)?; - let layout = Layout::create_relational_schema(&conn, site.clone(), schema)?; + let layout = Layout::create_relational_schema( + &conn, + site.clone(), + schema, + entities_with_causality_region.into_iter().collect(), + )?; // See if we are grafting and check that the graft is permissible if let Some(base) = graft_base { let errors = layout.can_copy_from(&base); @@ -274,7 +281,7 @@ impl DeploymentStore { .interfaces_for_type(&key.entity_type) .into_iter() .flatten() - .flat_map(|interface| &types_for_interface[&interface.into()]) + .flat_map(|interface| &types_for_interface[&EntityType::from(interface)]) .map(EntityType::from) .filter(|type_name| type_name != &key.entity_type), ); @@ -1043,17 +1050,17 @@ impl DeploymentStore { ) -> Result, StoreError> { let conn = self.get_conn()?; let layout = self.layout(&conn, site)?; - layout.find(&conn, &key.entity_type, &key.entity_id, block) + layout.find(&conn, &key, block) } - /// Retrieve all the entities matching `ids_for_type` from the - /// deployment `site`. Only consider entities as of the given `block` + /// Retrieve all the entities matching `ids_for_type`, both the type and causality region, from + /// the deployment `site`. Only consider entities as of the given `block` pub(crate) fn get_many( &self, site: Arc, - ids_for_type: &BTreeMap<&EntityType, Vec<&str>>, + ids_for_type: &BTreeMap<(EntityType, CausalityRegion), Vec>, block: BlockNumber, - ) -> Result>, StoreError> { + ) -> Result, StoreError> { if ids_for_type.is_empty() { return Ok(BTreeMap::new()); } diff --git a/store/postgres/src/detail.rs b/store/postgres/src/detail.rs index 8e6698d1b53..f67ffbae34a 100644 --- a/store/postgres/src/detail.rs +++ b/store/postgres/src/detail.rs @@ -10,6 +10,7 @@ use diesel::prelude::{ use diesel_derives::Associations; use git_testament::{git_testament, git_testament_macros}; use graph::blockchain::BlockHash; +use graph::components::store::EntityType; use graph::data::subgraph::schema::{SubgraphError, SubgraphManifestEntity}; use graph::prelude::{ bigdecimal::ToPrimitive, BigDecimal, BlockPtr, DeploymentHash, StoreError, @@ -338,6 +339,7 @@ struct StoredSubgraphManifest { start_block_number: Option, start_block_hash: Option, raw_yaml: Option, + entities_with_causality_region: Vec, } impl From for SubgraphManifestEntity { @@ -349,6 +351,7 @@ impl From for SubgraphManifestEntity { features: value.features, schema: value.schema, raw_yaml: value.raw_yaml, + entities_with_causality_region: value.entities_with_causality_region, } } } diff --git a/store/postgres/src/relational.rs b/store/postgres/src/relational.rs index c1a532a2fdf..4c038f79669 100644 --- a/store/postgres/src/relational.rs +++ b/store/postgres/src/relational.rs @@ -24,6 +24,7 @@ use graph::constraint_violation; use graph::data::graphql::TypeExt as _; use graph::data::query::Trace; use graph::data::value::Word; +use graph::data_source::CausalityRegion; use graph::prelude::{q, s, EntityQuery, StopwatchMetrics, ENV_VARS}; use graph::slog::warn; use inflector::Inflector; @@ -313,6 +314,9 @@ impl Layout { &enums, &id_types, i as u32, + catalog + .entities_with_causality_region + .contains(&EntityType::from(obj_type.clone())), ) }) .collect::, _>>()?; @@ -393,6 +397,7 @@ impl Layout { position: position as u32, is_account_like: false, immutable: false, + has_causality_region: false, } } @@ -404,8 +409,9 @@ impl Layout { conn: &PgConnection, site: Arc, schema: &Schema, + entities_with_causality_region: BTreeSet, ) -> Result { - let catalog = Catalog::for_creation(site.cheap_clone()); + let catalog = Catalog::for_creation(site.cheap_clone(), entities_with_causality_region); let layout = Self::new(site, schema, catalog)?; let sql = layout .as_ddl() @@ -484,31 +490,31 @@ impl Layout { pub fn find( &self, conn: &PgConnection, - entity: &EntityType, - id: &str, + key: &EntityKey, block: BlockNumber, ) -> Result, StoreError> { - let table = self.table_for_entity(entity)?; - FindQuery::new(table.as_ref(), id, block) + let table = self.table_for_entity(&key.entity_type)?; + FindQuery::new(table.as_ref(), key, block) .get_result::(conn) .optional()? .map(|entity_data| entity_data.deserialize_with_layout(self, None, true)) .transpose() } + // An optimization when looking up multiple entities, it will generate a single sql query using `UNION ALL`. pub fn find_many( &self, conn: &PgConnection, - ids_for_type: &BTreeMap<&EntityType, Vec<&str>>, + ids_for_type: &BTreeMap<(EntityType, CausalityRegion), Vec>, block: BlockNumber, - ) -> Result>, StoreError> { + ) -> Result, StoreError> { if ids_for_type.is_empty() { return Ok(BTreeMap::new()); } let mut tables = Vec::new(); - for entity_type in ids_for_type.keys() { - tables.push(self.table_for_entity(entity_type)?.as_ref()); + for (entity_type, cr) in ids_for_type.keys() { + tables.push((self.table_for_entity(entity_type)?.as_ref(), *cr)); } let query = FindManyQuery { _namespace: &self.catalog.site.namespace, @@ -516,17 +522,22 @@ impl Layout { tables, block, }; - let mut entities_for_type: BTreeMap> = BTreeMap::new(); + let mut entities: BTreeMap = BTreeMap::new(); for data in query.load::(conn)? { let entity_type = data.entity_type(); let entity_data: Entity = data.deserialize_with_layout(self, None, true)?; - entities_for_type - .entry(entity_type) - .or_default() - .push(entity_data); + let key = EntityKey { + entity_type, + entity_id: entity_data.id()?.into(), + causality_region: CausalityRegion::from_entity(&entity_data), + }; + let overwrite = entities.insert(key, entity_data).is_some(); + if overwrite { + return Err(constraint_violation!("duplicate entity in result set")); + } } - Ok(entities_for_type) + Ok(entities) } pub fn find_changes( @@ -553,18 +564,15 @@ impl Layout { for entity_data in inserts_or_updates.into_iter() { let entity_type = entity_data.entity_type(); - let mut data: Entity = entity_data.deserialize_with_layout(self, None, false)?; + let data: Entity = entity_data.deserialize_with_layout(self, None, true)?; let entity_id = Word::from(data.id().expect("Invalid ID for entity.")); processed_entities.insert((entity_type.clone(), entity_id.clone())); - // `__typename` is not a real field. - data.remove("__typename") - .expect("__typename expected; this is a bug"); - changes.push(EntityOperation::Set { key: EntityKey { entity_type, entity_id, + causality_region: CausalityRegion::from_entity(&data), }, data, }); @@ -581,6 +589,7 @@ impl Layout { key: EntityKey { entity_type, entity_id, + causality_region: del.causality_region(), }, }); } @@ -1215,6 +1224,10 @@ pub struct Table { /// Entities in this table are immutable, i.e., will never be updated or /// deleted pub(crate) immutable: bool, + + /// Whether this table has an explicit `causality_region` column. If `false`, then the column is + /// not present and the causality region for all rows is implicitly `0` (equivalent to CasualityRegion::ONCHAIN). + pub(crate) has_causality_region: bool, } impl Table { @@ -1225,6 +1238,7 @@ impl Table { enums: &EnumMap, id_types: &IdTypeMap, position: u32, + has_causality_region: bool, ) -> Result { SqlName::check_valid_identifier(&*defn.name, "object")?; @@ -1250,6 +1264,7 @@ impl Table { columns, position, immutable, + has_causality_region, }; Ok(table) } @@ -1265,6 +1280,7 @@ impl Table { is_account_like: self.is_account_like, position: self.position, immutable: self.immutable, + has_causality_region: self.has_causality_region, }; Arc::new(other) @@ -1379,7 +1395,8 @@ impl LayoutCache { fn load(conn: &PgConnection, site: Arc) -> Result, StoreError> { let (subgraph_schema, use_bytea_prefix) = deployment::schema(conn, site.as_ref())?; - let catalog = Catalog::load(conn, site.clone(), use_bytea_prefix)?; + let has_causality_region = deployment::entities_with_causality_region(conn, site.id)?; + let catalog = Catalog::load(conn, site.clone(), use_bytea_prefix, has_causality_region)?; let layout = Arc::new(Layout::new(site.clone(), &subgraph_schema, catalog)?); layout.refresh(conn, site) } diff --git a/store/postgres/src/relational/ddl.rs b/store/postgres/src/relational/ddl.rs index 4c9d6a18927..5b20be43d29 100644 --- a/store/postgres/src/relational/ddl.rs +++ b/store/postgres/src/relational/ddl.rs @@ -5,13 +5,21 @@ use std::{ use graph::prelude::BLOCK_NUMBER_MAX; +use crate::block_range::CAUSALITY_REGION_COLUMN; use crate::relational::{ - Catalog, ColumnType, BLOCK_COLUMN, BLOCK_RANGE_COLUMN, BYTE_ARRAY_PREFIX_SIZE, - STRING_PREFIX_SIZE, VID_COLUMN, + ColumnType, BLOCK_COLUMN, BLOCK_RANGE_COLUMN, BYTE_ARRAY_PREFIX_SIZE, STRING_PREFIX_SIZE, + VID_COLUMN, }; use super::{Column, Layout, SqlName, Table}; +// In debug builds (for testing etc.) unconditionally create exclusion constraints, in release +// builds for production, skip them +#[cfg(debug_assertions)] +const CREATE_EXCLUSION_CONSTRAINT: bool = true; +#[cfg(not(debug_assertions))] +const CREATE_EXCLUSION_CONSTRAINT: bool = false; + impl Layout { /// Generate the DDL for the entire layout, i.e., all `create table` /// and `create index` etc. statements needed in the database schema @@ -77,30 +85,38 @@ impl Table { fn columns_ddl(table: &Table) -> Result { let mut cols = String::new(); let mut first = true; + + if table.has_causality_region { + first = false; + write!( + cols, + "{causality_region} int not null", + causality_region = CAUSALITY_REGION_COLUMN + )?; + } + for column in &table.columns { if !first { writeln!(cols, ",")?; - } else { - writeln!(cols)?; + write!(cols, " ")?; } - write!(cols, " ")?; column.as_ddl(&mut cols)?; first = false; } + Ok(cols) } if self.immutable { writeln!( out, - r#" - create table {qname} ( - {vid} bigserial primary key, - {block} int not null, - {cols}, - unique({id}) - ); - "#, + " + create table {qname} ( + {vid} bigserial primary key, + {block} int not null,\n\ + {cols}, + unique({id}) + );", qname = self.qualified_name, cols = columns_ddl(self)?, vid = VID_COLUMN, @@ -111,19 +127,18 @@ impl Table { writeln!( out, r#" - create table {qname} ( - {vid} bigserial primary key, - {block_range} int4range not null, - {cols} - ); - "#, + create table {qname} ( + {vid} bigserial primary key, + {block_range} int4range not null, + {cols} + );"#, qname = self.qualified_name, cols = columns_ddl(self)?, vid = VID_COLUMN, block_range = BLOCK_RANGE_COLUMN )?; - self.exclusion_ddl(out, Catalog::create_exclusion_constraint()) + self.exclusion_ddl(out) } } @@ -266,14 +281,22 @@ impl Table { self.create_attribute_indexes(out) } - pub fn exclusion_ddl(&self, out: &mut String, as_constraint: bool) -> fmt::Result { + pub fn exclusion_ddl(&self, out: &mut String) -> fmt::Result { + // Tables with causality regions need to use exclusion constraints for correctness, + // to catch violations of write isolation. + let as_constraint = self.has_causality_region || CREATE_EXCLUSION_CONSTRAINT; + + self.exclusion_ddl_inner(out, as_constraint) + } + + // `pub` for tests. + pub(crate) fn exclusion_ddl_inner(&self, out: &mut String, as_constraint: bool) -> fmt::Result { if as_constraint { writeln!( out, - r#" - alter table {qname} - add constraint {bare_name}_{id}_{block_range}_excl exclude using gist ({id} with =, {block_range} with &&); - "#, + " + alter table {qname} + add constraint {bare_name}_{id}_{block_range}_excl exclude using gist ({id} with =, {block_range} with &&);", qname = self.qualified_name, bare_name = self.name, id = self.primary_key().name, @@ -282,10 +305,10 @@ impl Table { } else { writeln!( out, - r#" + " create index {bare_name}_{id}_{block_range}_excl on {qname} using gist ({id}, {block_range}); - "#, + ", qname = self.qualified_name, bare_name = self.name, id = self.primary_key().name, @@ -303,7 +326,6 @@ impl Column { /// See the unit tests at the end of this file for the actual DDL that /// gets generated fn as_ddl(&self, out: &mut String) -> fmt::Result { - write!(out, " ")?; write!(out, "{:20} {}", self.name.quoted(), self.sql_type())?; if self.is_list() { write!(out, "[]")?; diff --git a/store/postgres/src/relational/ddl_tests.rs b/store/postgres/src/relational/ddl_tests.rs index abcf8f1e595..72a3b8bed7b 100644 --- a/store/postgres/src/relational/ddl_tests.rs +++ b/store/postgres/src/relational/ddl_tests.rs @@ -1,4 +1,5 @@ use itertools::Itertools; +use pretty_assertions::assert_eq; use super::*; @@ -11,7 +12,8 @@ fn test_layout(gql: &str) -> Layout { let schema = Schema::parse(gql, subgraph.clone()).expect("Test schema invalid"); let namespace = Namespace::new("sgd0815".to_owned()).unwrap(); let site = Arc::new(make_dummy_site(subgraph, namespace, "anet".to_string())); - let catalog = Catalog::for_tests(site.clone()).expect("Can not create catalog"); + let catalog = Catalog::for_tests(site.clone(), BTreeSet::from_iter(["FileThing".into()])) + .expect("Can not create catalog"); Layout::new(site, &schema, catalog).expect("Failed to construct Layout") } @@ -57,7 +59,7 @@ fn check_eqv(left: &str, right: &str) { fn generate_ddl() { let layout = test_layout(THING_GQL); let sql = layout.as_ddl().expect("Failed to generate DDL"); - check_eqv(THING_DDL, &sql); + assert_eq!(THING_DDL, &sql); // Use `assert_eq!` to also test the formatting. let layout = test_layout(MUSIC_GQL); let sql = layout.as_ddl().expect("Failed to generate DDL"); @@ -86,7 +88,7 @@ fn exlusion_ddl() { // When `as_constraint` is false, just create an index let mut out = String::new(); table - .exclusion_ddl(&mut out, false) + .exclusion_ddl_inner(&mut out, false) .expect("can write exclusion DDL"); check_eqv( r#"create index thing_id_block_range_excl on "sgd0815"."thing" using gist (id, block_range);"#, @@ -96,7 +98,7 @@ fn exlusion_ddl() { // When `as_constraint` is true, add an exclusion constraint let mut out = String::new(); table - .exclusion_ddl(&mut out, true) + .exclusion_ddl_inner(&mut out, true) .expect("can write exclusion DDL"); check_eqv( r#"alter table "sgd0815"."thing" add constraint thing_id_block_range_excl exclude using gist (id with =, block_range with &&);"#, @@ -198,20 +200,27 @@ const THING_GQL: &str = r#" bytes: Bytes, bigInt: BigInt, color: Color, - }"#; + } + + type FileThing @entity { + id: ID! + } + "#; const THING_DDL: &str = r#"create type sgd0815."color" as enum ('BLUE', 'red', 'yellow'); create type sgd0815."size" as enum ('large', 'medium', 'small'); -create table "sgd0815"."thing" ( + + create table "sgd0815"."thing" ( vid bigserial primary key, block_range int4range not null, "id" text not null, "big_thing" text not null -); -alter table "sgd0815"."thing" - add constraint thing_id_block_range_excl exclude using gist (id with =, block_range with &&); + ); + + alter table "sgd0815"."thing" + add constraint thing_id_block_range_excl exclude using gist (id with =, block_range with &&); create index brin_thing on "sgd0815"."thing" using brin(lower(block_range), coalesce(upper(block_range), 2147483647), vid); @@ -223,7 +232,8 @@ create index attr_0_0_thing_id create index attr_0_1_thing_big_thing on "sgd0815"."thing" using gist("big_thing", block_range); -create table "sgd0815"."scalar" ( + + create table "sgd0815"."scalar" ( vid bigserial primary key, block_range int4range not null, "id" text not null, @@ -234,9 +244,10 @@ create table "sgd0815"."scalar" ( "bytes" bytea, "big_int" numeric, "color" "sgd0815"."color" -); -alter table "sgd0815"."scalar" - add constraint scalar_id_block_range_excl exclude using gist (id with =, block_range with &&); + ); + + alter table "sgd0815"."scalar" + add constraint scalar_id_block_range_excl exclude using gist (id with =, block_range with &&); create index brin_scalar on "sgd0815"."scalar" using brin(lower(block_range), coalesce(upper(block_range), 2147483647), vid); @@ -260,6 +271,25 @@ create index attr_1_6_scalar_big_int create index attr_1_7_scalar_color on "sgd0815"."scalar" using btree("color"); + + create table "sgd0815"."file_thing" ( + vid bigserial primary key, + block_range int4range not null, + causality_region int not null, + "id" text not null + ); + + alter table "sgd0815"."file_thing" + add constraint file_thing_id_block_range_excl exclude using gist (id with =, block_range with &&); +create index brin_file_thing + on "sgd0815"."file_thing" + using brin(lower(block_range), coalesce(upper(block_range), 2147483647), vid); +create index file_thing_block_range_closed + on "sgd0815"."file_thing"(coalesce(upper(block_range), 2147483647)) + where coalesce(upper(block_range), 2147483647) < 2147483647; +create index attr_2_0_file_thing_id + on "sgd0815"."file_thing" using btree("id"); + "#; const MUSIC_GQL: &str = r#"type Musician @entity { diff --git a/store/postgres/src/relational/query_tests.rs b/store/postgres/src/relational/query_tests.rs index 2c7cacaf07a..acb4610b301 100644 --- a/store/postgres/src/relational/query_tests.rs +++ b/store/postgres/src/relational/query_tests.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::BTreeSet, sync::Arc}; use diesel::{debug_query, pg::Pg}; use graph::{ @@ -33,7 +33,8 @@ fn test_layout(gql: &str) -> Layout { let schema = Schema::parse(gql, subgraph.clone()).expect("Test schema invalid"); let namespace = Namespace::new("sgd0815".to_owned()).unwrap(); let site = Arc::new(make_dummy_site(subgraph, namespace, "anet".to_string())); - let catalog = Catalog::for_tests(site.clone()).expect("Can not create catalog"); + let catalog = + Catalog::for_tests(site.clone(), BTreeSet::new()).expect("Can not create catalog"); Layout::new(site, &schema, catalog).expect("Failed to construct Layout") } diff --git a/store/postgres/src/relational_queries.rs b/store/postgres/src/relational_queries.rs index cbf85bbf973..e311835437b 100644 --- a/store/postgres/src/relational_queries.rs +++ b/store/postgres/src/relational_queries.rs @@ -14,6 +14,7 @@ use diesel::Connection; use graph::components::store::EntityKey; use graph::data::value::Word; +use graph::data_source::CausalityRegion; use graph::prelude::{ anyhow, r, serde_json, Attribute, BlockNumber, ChildMultiplicity, Entity, EntityCollection, EntityFilter, EntityLink, EntityOrder, EntityRange, EntityWindow, ParentLink, @@ -39,7 +40,7 @@ use crate::sql_value::SqlValue; use crate::{ block_range::{ BlockRangeColumn, BlockRangeLowerBoundClause, BlockRangeUpperBoundClause, BLOCK_COLUMN, - BLOCK_RANGE_COLUMN, BLOCK_RANGE_CURRENT, + BLOCK_RANGE_COLUMN, BLOCK_RANGE_CURRENT, CAUSALITY_REGION_COLUMN, }, primary::{Namespace, Site}, }; @@ -448,6 +449,8 @@ pub struct EntityDeletion { entity: String, #[sql_type = "Text"] id: String, + #[sql_type = "Integer"] + causality_region: CausalityRegion, } impl EntityDeletion { @@ -458,6 +461,10 @@ impl EntityDeletion { pub fn id(&self) -> &str { &self.id } + + pub fn causality_region(&self) -> CausalityRegion { + self.causality_region + } } /// Helper struct for retrieving entities from the database. With diesel, we @@ -1443,10 +1450,12 @@ impl<'a> QueryFragment for QueryFilter<'a> { } } +/// A query that finds an entity by key. Used during indexing. +/// See also `FindManyQuery`. #[derive(Debug, Clone, Constructor)] pub struct FindQuery<'a> { table: &'a Table, - id: &'a str, + key: &'a EntityKey, block: BlockNumber, } @@ -1454,6 +1463,12 @@ impl<'a> QueryFragment for FindQuery<'a> { fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { out.unsafe_to_cache_prepared(); + let EntityKey { + entity_type: _, + entity_id, + causality_region, + } = self.key; + // Generate // select '..' as entity, to_jsonb(e.*) as data // from schema.table e where id = $1 @@ -1463,8 +1478,13 @@ impl<'a> QueryFragment for FindQuery<'a> { out.push_sql(" from "); out.push_sql(self.table.qualified_name.as_str()); out.push_sql(" e\n where "); - self.table.primary_key().eq(self.id, &mut out)?; + self.table.primary_key().eq(entity_id, &mut out)?; out.push_sql(" and "); + if self.table.has_causality_region { + out.push_sql("causality_region = "); + out.push_bind_param::(causality_region)?; + out.push_sql(" and "); + } BlockRangeColumn::new(self.table, "e.", self.block).contains(&mut out) } } @@ -1553,7 +1573,13 @@ impl<'a> QueryFragment for FindPossibleDeletionsQuery<'a> { } out.push_sql("select "); out.push_bind_param::(&table.object.as_str())?; - out.push_sql(" as entity, e.id\n"); + out.push_sql(" as entity, "); + if table.has_causality_region { + out.push_sql("causality_region, "); + } else { + out.push_sql("0 as causality_region, "); + } + out.push_sql("e.id\n"); out.push_sql(" from "); out.push_sql(table.qualified_name.as_str()); out.push_sql(" e\n where "); @@ -1581,10 +1607,10 @@ impl<'a, Conn> RunQueryDsl for FindPossibleDeletionsQuery<'a> {} #[derive(Debug, Clone, Constructor)] pub struct FindManyQuery<'a> { pub(crate) _namespace: &'a Namespace, - pub(crate) tables: Vec<&'a Table>, + pub(crate) tables: Vec<(&'a Table, CausalityRegion)>, // Maps object name to ids. - pub(crate) ids_for_type: &'a BTreeMap<&'a EntityType, Vec<&'a str>>, + pub(crate) ids_for_type: &'a BTreeMap<(EntityType, CausalityRegion), Vec>, pub(crate) block: BlockNumber, } @@ -1600,7 +1626,7 @@ impl<'a> QueryFragment for FindManyQuery<'a> { // from schema. e where {id.is_in($ids1)) // union all // ... - for (i, table) in self.tables.iter().enumerate() { + for (i, (table, cr)) in self.tables.iter().enumerate() { if i > 0 { out.push_sql("\nunion all\n"); } @@ -1612,8 +1638,13 @@ impl<'a> QueryFragment for FindManyQuery<'a> { out.push_sql(" e\n where "); table .primary_key() - .is_in(&self.ids_for_type[&table.object], &mut out)?; + .is_in(&self.ids_for_type[&(table.object.clone(), *cr)], &mut out)?; out.push_sql(" and "); + if table.has_causality_region { + out.push_sql("causality_region = "); + out.push_bind_param::(cr)?; + out.push_sql(" and "); + } BlockRangeColumn::new(table, "e.", self.block).contains(&mut out)?; } Ok(()) @@ -1745,12 +1776,16 @@ impl<'a> QueryFragment for InsertQuery<'a> { out.push_sql(", "); } self.br_column.name(&mut out); + if self.table.has_causality_region { + out.push_sql(", "); + out.push_sql(CAUSALITY_REGION_COLUMN); + }; out.push_sql(") values\n"); // Use a `Peekable` iterator to help us decide how to finalize each line. - let mut iter = self.entities.iter().map(|(_key, entity)| entity).peekable(); - while let Some(entity) = iter.next() { + let mut iter = self.entities.iter().peekable(); + while let Some((key, entity)) = iter.next() { out.push_sql("("); for column in &self.unique_columns { // If the column name is not within this entity's fields, we will issue the @@ -1763,6 +1798,10 @@ impl<'a> QueryFragment for InsertQuery<'a> { out.push_sql(", "); } self.br_column.literal_range_current(&mut out)?; + if self.table.has_causality_region { + out.push_sql(", "); + out.push_bind_param::(&key.causality_region)?; + }; out.push_sql(")"); // finalize line according to remaining entities to insert @@ -3488,6 +3527,12 @@ impl<'a> QueryFragment for CopyEntityBatchQuery<'a> { } else { out.push_sql(BLOCK_RANGE_COLUMN); } + + if self.dst.has_causality_region { + out.push_sql(", "); + out.push_sql(CAUSALITY_REGION_COLUMN); + }; + out.push_sql(")\nselect "); for column in &self.columns { out.push_identifier(column.name.as_str())?; @@ -3534,6 +3579,24 @@ impl<'a> QueryFragment for CopyEntityBatchQuery<'a> { } (false, false) => out.push_sql(BLOCK_RANGE_COLUMN), } + + match (self.src.has_causality_region, self.dst.has_causality_region) { + (false, false) => (), + (true, true) => { + out.push_sql(", "); + out.push_sql(CAUSALITY_REGION_COLUMN); + } + (false, true) => { + out.push_sql(", 0"); + } + (true, false) => { + return Err(constraint_violation!( + "can not copy entity type {} to {} because the src has a causality region but the dst does not", + self.src.object.as_str(), + self.dst.object.as_str() + )); + } + } out.push_sql(" from "); out.push_sql(self.src.qualified_name.as_str()); out.push_sql(" where vid >= "); diff --git a/store/postgres/src/writable.rs b/store/postgres/src/writable.rs index 926ad38a43a..204af9f7fe3 100644 --- a/store/postgres/src/writable.rs +++ b/store/postgres/src/writable.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; use std::time::Duration; @@ -274,12 +275,20 @@ impl SyncStore { fn get_many( &self, - ids_for_type: BTreeMap<&EntityType, Vec<&str>>, + keys: BTreeSet, block: BlockNumber, - ) -> Result>, StoreError> { + ) -> Result, StoreError> { + let mut by_type: BTreeMap<(EntityType, CausalityRegion), Vec> = BTreeMap::new(); + for key in keys { + by_type + .entry((key.entity_type, key.causality_region)) + .or_default() + .push(key.entity_id.into()); + } + self.retry("get_many", || { self.writable - .get_many(self.site.cheap_clone(), &ids_for_type, block) + .get_many(self.site.cheap_clone(), &by_type, block) }) } @@ -708,15 +717,15 @@ impl Queue { /// Get many entities at once by looking at both the queue and the store fn get_many( &self, - mut ids_for_type: BTreeMap<&EntityType, Vec<&str>>, - ) -> Result>, StoreError> { + mut keys: BTreeSet, + ) -> Result, StoreError> { // See the implementation of `get` for how we handle reverts let mut tracker = BlockTracker::new(); // Get entities from entries in the queue - let mut map = self.queue.fold( + let entities_in_queue = self.queue.fold( BTreeMap::new(), - |mut map: BTreeMap>, req| { + |mut map: BTreeMap>, req| { tracker.update(req.as_ref()); match req.as_ref() { Request::Write { @@ -725,26 +734,9 @@ impl Queue { if tracker.visible(block_ptr) { for emod in mods { let key = emod.entity_ref(); - if let Some(ids) = ids_for_type.get_mut(&key.entity_type) { - if let Some(idx) = - ids.iter().position(|id| *id == key.entity_id.as_str()) - { - // We are looking for the entity - // underlying this modification. Add - // it to the result map, but also - // remove it from `ids_for_type` so - // that we don't look for it any - // more - if let Some(entity) = emod.entity() { - map.entry(key.entity_type.clone()) - .or_default() - .push(entity.clone()); - } - ids.swap_remove(idx); - if ids.is_empty() { - ids_for_type.remove(&key.entity_type); - } - } + // The key must be removed to avoid overwriting it with a stale value. + if let Some(key) = keys.take(key) { + map.insert(key, emod.entity().cloned()); } } } @@ -755,20 +747,17 @@ impl Queue { }, ); - // Whatever remains in `ids_for_type` needs to be gotten from the - // store. Take extra care to not unnecessarily copy maps - if !ids_for_type.is_empty() { - let store_map = self.store.get_many(ids_for_type, tracker.query_block())?; - if !store_map.is_empty() { - if map.is_empty() { - map = store_map - } else { - for (entity_type, mut entities) in store_map { - map.entry(entity_type).or_default().append(&mut entities); - } - } + // Whatever remains in `keys` needs to be gotten from the store + let mut map = self.store.get_many(keys, tracker.query_block())?; + + // Extend the store results with the entities from the queue. + for (key, entity) in entities_in_queue { + if let Some(entity) = entity { + let overwrite = map.insert(key, entity).is_some(); + assert!(!overwrite); } } + Ok(map) } @@ -921,11 +910,11 @@ impl Writer { fn get_many( &self, - ids_for_type: BTreeMap<&EntityType, Vec<&str>>, - ) -> Result>, StoreError> { + keys: BTreeSet, + ) -> Result, StoreError> { match self { - Writer::Sync(store) => store.get_many(ids_for_type, BLOCK_NUMBER_MAX), - Writer::Async(queue) => queue.get_many(ids_for_type), + Writer::Sync(store) => store.get_many(keys, BLOCK_NUMBER_MAX), + Writer::Async(queue) => queue.get_many(keys), } } @@ -1006,9 +995,9 @@ impl ReadStore for WritableStore { fn get_many( &self, - ids_for_type: BTreeMap<&EntityType, Vec<&str>>, - ) -> Result>, StoreError> { - self.writer.get_many(ids_for_type) + keys: BTreeSet, + ) -> Result, StoreError> { + self.writer.get_many(keys) } fn input_schema(&self) -> Arc { diff --git a/store/postgres/tests/graft.rs b/store/postgres/tests/graft.rs index 4a7539b61af..660661778e0 100644 --- a/store/postgres/tests/graft.rs +++ b/store/postgres/tests/graft.rs @@ -259,10 +259,7 @@ fn create_test_entity( ); EntityOperation::Set { - key: EntityKey { - entity_type: EntityType::new(entity_type.to_string()), - entity_id: id.into(), - }, + key: EntityKey::data(entity_type.to_string(), id), data: test_entity, } } @@ -325,10 +322,7 @@ async fn check_graft( // Make our own entries for block 2 shaq.set("email", "shaq@gmail.com"); let op = EntityOperation::Set { - key: EntityKey { - entity_type: EntityType::new(USER.to_owned()), - entity_id: "3".into(), - }, + key: EntityKey::data(USER.to_owned(), "3"), data: shaq, }; transact_and_wait(&store, &deployment, BLOCKS[2].clone(), vec![op]) diff --git a/store/postgres/tests/relational.rs b/store/postgres/tests/relational.rs index 4cf67adeca5..986b4cde872 100644 --- a/store/postgres/tests/relational.rs +++ b/store/postgres/tests/relational.rs @@ -16,6 +16,7 @@ use graph_store_postgres::layout_for_tests::SqlName; use hex_literal::hex; use lazy_static::lazy_static; use std::borrow::Cow; +use std::collections::BTreeSet; use std::panic; use std::str::FromStr; use std::sync::Arc; @@ -426,7 +427,7 @@ fn create_schema(conn: &PgConnection) -> Layout { let query = format!("create schema {}", NAMESPACE.as_str()); conn.batch_execute(&*query).unwrap(); - Layout::create_relational_schema(&conn, Arc::new(site), &schema) + Layout::create_relational_schema(&conn, Arc::new(site), &schema, BTreeSet::new()) .expect("Failed to create relational schema") } @@ -494,19 +495,31 @@ fn find() { // Happy path: find existing entity let entity = layout - .find(conn, &*SCALAR, "one", BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(SCALAR.as_str(), "one"), + BLOCK_NUMBER_MAX, + ) .expect("Failed to read Scalar[one]") .unwrap(); assert_entity_eq!(scrub(&*SCALAR_ENTITY), entity); // Find non-existing entity let entity = layout - .find(conn, &*SCALAR, "noone", BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(SCALAR.as_str(), "noone"), + BLOCK_NUMBER_MAX, + ) .expect("Failed to read Scalar[noone]"); assert!(entity.is_none()); // Find for non-existing entity type - let err = layout.find(conn, &*NO_ENTITY, "one", BLOCK_NUMBER_MAX); + let err = layout.find( + conn, + &EntityKey::data(NO_ENTITY.as_str(), "one"), + BLOCK_NUMBER_MAX, + ); match err { Err(e) => assert_eq!("unknown table 'NoEntity'", e.to_string()), _ => { @@ -529,7 +542,11 @@ fn insert_null_fulltext_fields() { // Find entity with null string values let entity = layout - .find(conn, &*NULLABLE_STRINGS, "one", BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(NULLABLE_STRINGS.as_str(), "one"), + BLOCK_NUMBER_MAX, + ) .expect("Failed to read NullableStrings[one]") .unwrap(); assert_entity_eq!(scrub(&*EMPTY_NULLABLESTRINGS_ENTITY), entity); @@ -555,7 +572,11 @@ fn update() { .expect("Failed to update"); let actual = layout - .find(conn, &*SCALAR, "one", BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(SCALAR.as_str(), "one"), + BLOCK_NUMBER_MAX, + ) .expect("Failed to read Scalar[one]") .unwrap(); assert_entity_eq!(scrub(&entity), actual); @@ -611,9 +632,13 @@ fn update_many() { // check updates took effect let updated: Vec = ["one", "two", "three"] .iter() - .map(|id| { + .map(|&id| { layout - .find(conn, &*SCALAR, id, BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(SCALAR.as_str(), id), + BLOCK_NUMBER_MAX, + ) .expect(&format!("Failed to read Scalar[{}]", id)) .unwrap() }) @@ -679,7 +704,11 @@ fn serialize_bigdecimal() { .expect("Failed to update"); let actual = layout - .find(conn, &*SCALAR, "one", BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(SCALAR.as_str(), "one"), + BLOCK_NUMBER_MAX, + ) .expect("Failed to read Scalar[one]") .unwrap(); assert_entity_eq!(entity, actual); @@ -880,7 +909,7 @@ fn revert_block() { let assert_fred = |name: &str| { let fred = layout - .find(conn, &EntityType::from("Cat"), id, BLOCK_NUMBER_MAX) + .find(conn, &EntityKey::data("Cat", id), BLOCK_NUMBER_MAX) .unwrap() .expect("there's a fred"); assert_eq!(name, fred.get("name").unwrap().as_str().unwrap()) diff --git a/store/postgres/tests/relational_bytes.rs b/store/postgres/tests/relational_bytes.rs index 7198085e9e4..663084a7100 100644 --- a/store/postgres/tests/relational_bytes.rs +++ b/store/postgres/tests/relational_bytes.rs @@ -3,11 +3,13 @@ use diesel::connection::SimpleConnection as _; use diesel::pg::PgConnection; use graph::components::store::EntityKey; use graph::data::store::scalar; +use graph::data_source::CausalityRegion; use graph::prelude::EntityQuery; use graph_mock::MockMetricsRegistry; use hex_literal::hex; use lazy_static::lazy_static; use std::borrow::Cow; +use std::collections::BTreeSet; use std::str::FromStr; use std::{collections::BTreeMap, sync::Arc}; @@ -127,7 +129,7 @@ fn create_schema(conn: &PgConnection) -> Layout { NAMESPACE.clone(), NETWORK_NAME.to_string(), ); - Layout::create_relational_schema(conn, Arc::new(site), &schema) + Layout::create_relational_schema(conn, Arc::new(site), &schema, BTreeSet::new()) .expect("Failed to create relational schema") } @@ -193,7 +195,11 @@ fn bad_id() { // We test that we get errors for various strings that are not // valid 'Bytes' strings; we use `find` to force the conversion // from String -> Bytes internally - let res = layout.find(conn, &*THING, "bad", BLOCK_NUMBER_MAX); + let res = layout.find( + conn, + &EntityKey::data(THING.as_str(), "bad"), + BLOCK_NUMBER_MAX, + ); assert!(res.is_err()); assert_eq!( "store error: Odd number of digits", @@ -201,7 +207,11 @@ fn bad_id() { ); // We do not allow the `\x` prefix that Postgres uses - let res = layout.find(conn, &*THING, "\\xbadd", BLOCK_NUMBER_MAX); + let res = layout.find( + conn, + &EntityKey::data(THING.as_str(), "\\xbadd"), + BLOCK_NUMBER_MAX, + ); assert!(res.is_err()); assert_eq!( "store error: Invalid character \'\\\\\' at position 0", @@ -209,11 +219,19 @@ fn bad_id() { ); // Having the '0x' prefix is ok - let res = layout.find(conn, &*THING, "0xbadd", BLOCK_NUMBER_MAX); + let res = layout.find( + conn, + &EntityKey::data(THING.as_str(), "0xbadd"), + BLOCK_NUMBER_MAX, + ); assert!(res.is_ok()); // Using non-hex characters is also bad - let res = layout.find(conn, &*THING, "nope", BLOCK_NUMBER_MAX); + let res = layout.find( + conn, + &EntityKey::data(THING.as_str(), "nope"), + BLOCK_NUMBER_MAX, + ); assert!(res.is_err()); assert_eq!( "store error: Invalid character \'n\' at position 0", @@ -231,14 +249,19 @@ fn find() { // Happy path: find existing entity let entity = layout - .find(conn, &*THING, ID, BLOCK_NUMBER_MAX) + .find(conn, &EntityKey::data(THING.as_str(), ID), BLOCK_NUMBER_MAX) .expect("Failed to read Thing[deadbeef]") .unwrap(); assert_entity_eq!(scrub(&*BEEF_ENTITY), entity); + assert!(CausalityRegion::from_entity(&entity) == CausalityRegion::ONCHAIN); // Find non-existing entity let entity = layout - .find(conn, &*THING, "badd", BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(THING.as_str(), "badd"), + BLOCK_NUMBER_MAX, + ) .expect("Failed to read Thing[badd]"); assert!(entity.is_none()); }); @@ -254,24 +277,29 @@ fn find_many() { insert_thing(conn, layout, ID, NAME); insert_thing(conn, layout, ID2, NAME2); - let mut id_map: BTreeMap<&EntityType, Vec<&str>> = BTreeMap::default(); - id_map.insert(&*THING, vec![ID, ID2, "badd"]); + let mut id_map = BTreeMap::default(); + id_map.insert( + (THING.clone(), CausalityRegion::ONCHAIN), + vec![ID.to_string(), ID2.to_string(), "badd".to_string()], + ); let entities = layout .find_many(conn, &id_map, BLOCK_NUMBER_MAX) .expect("Failed to read many things"); - assert_eq!(1, entities.len()); - - let ids = entities - .get(&*THING) - .expect("We got some things") - .iter() - .map(|thing| thing.id().unwrap()) - .collect::>(); - - assert_eq!(2, ids.len()); - assert!(ids.contains(&ID.to_owned()), "Missing ID"); - assert!(ids.contains(&ID2.to_owned()), "Missing ID2"); + assert_eq!(2, entities.len()); + + let id_key = EntityKey { + entity_id: ID.into(), + entity_type: THING.clone(), + causality_region: CausalityRegion::ONCHAIN, + }; + let id2_key = EntityKey { + entity_id: ID2.into(), + entity_type: THING.clone(), + causality_region: CausalityRegion::ONCHAIN, + }; + assert!(entities.contains_key(&id_key), "Missing ID"); + assert!(entities.contains_key(&id2_key), "Missing ID2"); }); } @@ -293,7 +321,11 @@ fn update() { .expect("Failed to update"); let actual = layout - .find(conn, &*THING, &entity_id, BLOCK_NUMBER_MAX) + .find( + conn, + &EntityKey::data(THING.as_str(), &entity_id), + BLOCK_NUMBER_MAX, + ) .expect("Failed to read Thing[deadbeef]") .unwrap(); diff --git a/store/test-store/src/store.rs b/store/test-store/src/store.rs index c3d00035988..513af19aca6 100644 --- a/store/test-store/src/store.rs +++ b/store/test-store/src/store.rs @@ -3,6 +3,7 @@ use graph::data::graphql::effort::LoadManager; use graph::data::query::QueryResults; use graph::data::query::QueryTarget; use graph::data::subgraph::schema::{DeploymentCreate, SubgraphError}; +use graph::data_source::CausalityRegion; use graph::log; use graph::prelude::{QueryStoreManager as _, SubgraphStore as _, *}; use graph::semver::Version; @@ -355,6 +356,7 @@ pub async fn insert_entities( key: EntityKey { entity_type, entity_id: data.get("id").unwrap().clone().as_string().unwrap().into(), + causality_region: CausalityRegion::ONCHAIN, }, data, }); diff --git a/tests/Cargo.toml b/tests/Cargo.toml index b0893f97ba9..cdc636c546e 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -24,6 +24,7 @@ hex = "0.4.3" serde_yaml = "0.8" hyper = "0.14" serde = "1.0" +assert-json-diff = "2.0.2" [dev-dependencies] bollard = "0.10" diff --git a/tests/integration-tests/file-data-sources/abis/Contract.abi b/tests/integration-tests/file-data-sources/abis/Contract.abi index 1e3ec7217af..9d9f56b9263 100644 --- a/tests/integration-tests/file-data-sources/abis/Contract.abi +++ b/tests/integration-tests/file-data-sources/abis/Contract.abi @@ -1 +1,15 @@ -[ ] +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "testCommand", + "type": "string" + } + ], + "name": "TestEvent", + "type": "event" + } +] diff --git a/tests/integration-tests/file-data-sources/src/mapping.ts b/tests/integration-tests/file-data-sources/src/mapping.ts index f928b1ba419..dbd8013908d 100644 --- a/tests/integration-tests/file-data-sources/src/mapping.ts +++ b/tests/integration-tests/file-data-sources/src/mapping.ts @@ -1,16 +1,31 @@ import { ethereum, dataSource, BigInt, Bytes, DataSourceContext } from '@graphprotocol/graph-ts' +import { TestEvent } from '../generated/Contract/Contract' import { IpfsFile, IpfsFile1 } from '../generated/schema' +// CID of `file-data-sources/abis/Contract.abi` after being processed by graph-cli. +const KNOWN_CID = "QmQ2REmceVtzawp7yrnxLQXgNNCtFHEnig6fL9aqE1kcWq" + export function handleBlock(block: ethereum.Block): void { + let entity = new IpfsFile("onchain") + entity.content = "onchain" + entity.save() + // This will create the same data source twice, once at block 0 and another at block 2. // The creation at block 2 should be detected as a duplicate and therefore a noop. if (block.number == BigInt.fromI32(0) || block.number == BigInt.fromI32(2)) { - // CID QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ is the file - // `file-data-sources/abis/Contract.abi` after being processed by graph-cli. - dataSource.create("File", ["QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ"]) + dataSource.create("File", [KNOWN_CID]) } if (block.number == BigInt.fromI32(1)) { + let entity = IpfsFile.load("onchain")! + assert(entity.content == "onchain") + + // The test assumes file data sources are processed in the block in which they are created. + // So the ds created at block 0 will have been processed. + // + // Test that onchain data sources cannot read offchain data. + assert(IpfsFile.load(KNOWN_CID) == null); + // Test that using an invalid CID will be ignored dataSource.create("File", ["hi, I'm not valid"]) } @@ -19,19 +34,50 @@ export function handleBlock(block: ethereum.Block): void { // This will invoke File1 data source with same CID, which will be used // to test whether same cid is triggered across different data source. if (block.number == BigInt.fromI32(3)) { - dataSource.create("File1", ["QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ"]) + // Test that onchain data sources cannot read offchain data (again, but this time more likely to hit the DB than the write queue). + assert(IpfsFile.load(KNOWN_CID) == null); + + dataSource.create("File1", [KNOWN_CID]) } +} + +export function handleTestEvent(event: TestEvent): void { + let command = event.params.testCommand; - // Will fail the subgraph when processed due to mismatch in the entity type and 'entities'. - if (block.number == BigInt.fromI32(5)) { - dataSource.create("File2", ["QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ"]) + if (command == "createFile2") { + // Will fail the subgraph when processed due to mismatch in the entity type and 'entities'. + dataSource.create("File2", [KNOWN_CID]) + } else if (command == "saveConflictingEntity") { + // Will fail the subgraph because the same entity has been created in a file data source. + let entity = new IpfsFile(KNOWN_CID) + entity.content = "empty" + entity.save() + } else if (command == "createFile1") { + // Will fail the subgraph with a conflict between two entities created by offchain data sources. + let context = new DataSourceContext(); + context.setBytes("hash", event.block.hash); + dataSource.createWithContext("File1", [KNOWN_CID], context) + } else { + assert(false, "Unknown command: " + command); } } export function handleFile(data: Bytes): void { + // Test that offchain data sources cannot read onchain data. + assert(IpfsFile.load("onchain") == null); + + if (dataSource.stringParam() != "QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ") { + // Test that an offchain data source cannot read from another offchain data source. + assert(IpfsFile.load("QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ") == null); + } + let entity = new IpfsFile(dataSource.stringParam()) entity.content = data.toString() entity.save() + + // Test that an offchain data source can load its own entities + let loaded_entity = IpfsFile.load(dataSource.stringParam())! + assert(loaded_entity.content == entity.content) } export function handleFile1(data: Bytes): void { diff --git a/tests/integration-tests/file-data-sources/subgraph.yaml b/tests/integration-tests/file-data-sources/subgraph.yaml index ee9d3aac689..502faccdff6 100644 --- a/tests/integration-tests/file-data-sources/subgraph.yaml +++ b/tests/integration-tests/file-data-sources/subgraph.yaml @@ -6,7 +6,7 @@ dataSources: name: Contract network: test source: - address: "0xCfEB869F69431e42cdB54A4F4f105C19C080A601" + address: "0x0000000000000000000000000000000000000000" abi: Contract mapping: kind: ethereum/events @@ -19,6 +19,9 @@ dataSources: file: ./abis/Contract.abi blockHandlers: - handler: handleBlock + eventHandlers: + - event: TestEvent(string) + handler: handleTestEvent file: ./src/mapping.ts templates: - kind: file/ipfs diff --git a/tests/src/fixture.rs b/tests/src/fixture.rs index f0487a65a5b..654bea65014 100644 --- a/tests/src/fixture.rs +++ b/tests/src/fixture.rs @@ -99,14 +99,36 @@ pub async fn build_subgraph_with_yarn_cmd(dir: &str, yarn_cmd: &str) -> Deployme } pub fn test_ptr(n: BlockNumber) -> BlockPtr { + test_ptr_reorged(n, 0) +} + +// Set n as the low bits and `reorg_n` as the high bits of the hash. +pub fn test_ptr_reorged(n: BlockNumber, reorg_n: u32) -> BlockPtr { + let mut hash = H256::from_low_u64_be(n as u64); + hash[0..4].copy_from_slice(&reorg_n.to_be_bytes()); BlockPtr { - hash: H256::from_low_u64_be(n as u64).into(), + hash: hash.into(), number: n, } } type GraphQlRunner = graph_graphql::prelude::GraphQlRunner; +pub struct TestChain { + pub chain: Arc, + pub block_stream_builder: Arc>, +} + +impl TestChain { + pub fn set_block_stream(&self, blocks: Vec>) + where + C::TriggerData: Clone, + { + let static_block_stream = Arc::new(StaticStreamBuilder { chain: blocks }); + *self.block_stream_builder.0.lock().unwrap() = static_block_stream; + } +} + pub struct TestContext { pub logger: Logger, pub provider: Arc< @@ -120,6 +142,7 @@ pub struct TestContext { pub instance_manager: SubgraphInstanceManager, pub link_resolver: Arc, pub env_vars: Arc, + pub ipfs: IpfsClient, graphql_runner: Arc, indexing_status_service: Arc>, } @@ -184,6 +207,9 @@ impl TestContext { } pub async fn start_and_sync_to(&self, stop_block: BlockPtr) { + // In case the subgraph has been previously started. + self.provider.stop(self.deployment.clone()).await.unwrap(); + self.provider .start(self.deployment.clone(), Some(stop_block.number)) .await @@ -200,8 +226,11 @@ impl TestContext { } pub async fn start_and_sync_to_error(&self, stop_block: BlockPtr) -> SubgraphError { + // In case the subgraph has been previously started. + self.provider.stop(self.deployment.clone()).await.unwrap(); + self.provider - .start(self.deployment.clone(), Some(stop_block.number)) + .start(self.deployment.clone(), None) .await .expect("unable to start subgraph"); @@ -259,6 +288,12 @@ impl TestContext { serde_json::from_str(&serde_json::to_string(&value).unwrap()).unwrap(); query_res.indexing_status_for_current_version } + + pub fn rewind(&self, block_ptr_to: BlockPtr) { + self.store + .rewind(self.deployment.hash.clone(), block_ptr_to) + .unwrap() + } } impl Drop for TestContext { @@ -332,7 +367,7 @@ pub async fn setup( subgraph_name: SubgraphName, hash: &DeploymentHash, stores: &Stores, - chain: Arc, + chain: &TestChain, graft_block: Option, env_vars: Option, ) -> TestContext { @@ -351,7 +386,7 @@ pub async fn setup( cleanup(&subgraph_store, &subgraph_name, hash).unwrap(); let mut blockchain_map = BlockchainMap::new(); - blockchain_map.insert(stores.network_name.clone(), chain); + blockchain_map.insert(stores.network_name.clone(), chain.chain.clone()); let static_filters = env_vars.experimental_static_filters; @@ -361,7 +396,7 @@ pub async fn setup( Default::default(), )); let ipfs_service = ipfs_service( - ipfs, + ipfs.cheap_clone(), env_vars.mappings.max_ipfs_file_bytes as u64, env_vars.mappings.ipfs_timeout, env_vars.mappings.ipfs_request_limit, @@ -445,6 +480,7 @@ pub async fn setup( link_resolver, env_vars, indexing_status_service, + ipfs, } } @@ -481,8 +517,11 @@ pub async fn wait_for_sync( }; let status = store.status_for_id(deployment.id); + if let Some(fatal_error) = status.fatal_error { - return Err(fatal_error); + if fatal_error.block_ptr.as_ref().unwrap() == &stop_block { + return Err(fatal_error); + } } if block_ptr == stop_block { @@ -514,6 +553,48 @@ impl BlockRefetcher for StaticBlockRefetcher { } } +pub struct MutexBlockStreamBuilder(pub Mutex>>); + +#[async_trait] +impl BlockStreamBuilder for MutexBlockStreamBuilder { + async fn build_firehose( + &self, + chain: &C, + deployment: DeploymentLocator, + block_cursor: FirehoseCursor, + start_blocks: Vec, + subgraph_current_block: Option, + filter: Arc<::TriggerFilter>, + unified_api_version: graph::data::subgraph::UnifiedMappingApiVersion, + ) -> anyhow::Result>> { + let builder = self.0.lock().unwrap().clone(); + + builder + .build_firehose( + chain, + deployment, + block_cursor, + start_blocks, + subgraph_current_block, + filter, + unified_api_version, + ) + .await + } + + async fn build_polling( + &self, + _chain: Arc, + _deployment: DeploymentLocator, + _start_blocks: Vec, + _subgraph_current_block: Option, + _filter: Arc<::TriggerFilter>, + _unified_api_version: graph::data::subgraph::UnifiedMappingApiVersion, + ) -> anyhow::Result>> { + unimplemented!("only firehose mode should be used for tests") + } +} + /// `chain` is the sequence of chain heads to be processed. If the next block to be processed in the /// chain is not a descendant of the previous one, reorgs will be emitted until it is. /// diff --git a/tests/src/fixture/ethereum.rs b/tests/src/fixture/ethereum.rs index 5d941b45c0d..3e94385ece5 100644 --- a/tests/src/fixture/ethereum.rs +++ b/tests/src/fixture/ethereum.rs @@ -1,16 +1,17 @@ use std::marker::PhantomData; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; use super::{ - test_ptr, NoopAdapterSelector, NoopRuntimeAdapter, StaticBlockRefetcher, StaticStreamBuilder, - Stores, NODE_ID, + test_ptr, MutexBlockStreamBuilder, NoopAdapterSelector, NoopRuntimeAdapter, + StaticBlockRefetcher, StaticStreamBuilder, Stores, TestChain, NODE_ID, }; use graph::blockchain::{BlockPtr, TriggersAdapterSelector}; use graph::cheap_clone::CheapClone; use graph::firehose::{FirehoseEndpoint, FirehoseEndpoints}; use graph::prelude::ethabi::ethereum_types::H256; -use graph::prelude::{LightEthereumBlock, LoggerFactory, NodeId}; +use graph::prelude::web3::types::{Address, Log, Transaction, H160}; +use graph::prelude::{ethabi, tiny_keccak, LightEthereumBlock, LoggerFactory, NodeId}; use graph::{blockchain::block_stream::BlockWithTriggers, prelude::ethabi::ethereum_types::U64}; use graph_chain_ethereum::network::EthereumNetworkAdapters; use graph_chain_ethereum::{ @@ -24,7 +25,7 @@ pub async fn chain( blocks: Vec>, stores: &Stores, triggers_adapter: Option>>, -) -> Chain { +) -> TestChain { let triggers_adapter = triggers_adapter.unwrap_or(Arc::new(NoopAdapterSelector { triggers_in_block_sleep: Duration::ZERO, x: PhantomData, @@ -47,7 +48,10 @@ pub async fn chain( ))] .into(); - Chain::new( + let static_block_stream = Arc::new(StaticStreamBuilder { chain: blocks }); + let block_stream_builder = Arc::new(MutexBlockStreamBuilder(Mutex::new(static_block_stream))); + + let chain = Chain::new( logger_factory.clone(), stores.network_name.clone(), node_id, @@ -57,14 +61,19 @@ pub async fn chain( firehose_endpoints, EthereumNetworkAdapters { adapters: vec![] }, stores.chain_head_listener.cheap_clone(), - Arc::new(StaticStreamBuilder { chain: blocks }), + block_stream_builder.clone(), Arc::new(StaticBlockRefetcher { x: PhantomData }), triggers_adapter, Arc::new(NoopRuntimeAdapter { x: PhantomData }), ENV_VARS.reorg_threshold, // We assume the tested chain is always ingestible for now true, - ) + ); + + TestChain { + chain: Arc::new(chain), + block_stream_builder, + } } pub fn genesis() -> BlockWithTriggers { @@ -86,13 +95,44 @@ pub fn empty_block( assert!(ptr != parent_ptr); assert!(ptr.number > parent_ptr.number); + // A 0x000.. transaction is used so `push_test_log` can use it + let transactions = vec![Transaction { + hash: H256::zero(), + block_hash: Some(H256::from_slice(ptr.hash.as_slice().into())), + block_number: Some(ptr.number.into()), + transaction_index: Some(0.into()), + from: Some(H160::zero()), + to: Some(H160::zero()), + ..Default::default() + }]; + BlockWithTriggers:: { block: BlockFinality::Final(Arc::new(LightEthereumBlock { hash: Some(H256::from_slice(ptr.hash.as_slice())), number: Some(U64::from(ptr.number)), parent_hash: H256::from_slice(parent_ptr.hash.as_slice()), + transactions, ..Default::default() })), trigger_data: vec![EthereumTrigger::Block(ptr, EthereumBlockTriggerType::Every)], } } + +pub fn push_test_log(block: &mut BlockWithTriggers, payload: impl Into) { + block.trigger_data.push(EthereumTrigger::Log( + Arc::new(Log { + address: Address::zero(), + topics: vec![tiny_keccak::keccak256(b"TestEvent(string)").into()], + data: ethabi::encode(&[ethabi::Token::String(payload.into())]).into(), + block_hash: Some(H256::from_slice(block.ptr().hash.as_slice())), + block_number: Some(block.ptr().number.into()), + transaction_hash: Some(H256::from_low_u64_be(0).into()), + transaction_index: Some(0.into()), + log_index: Some(0.into()), + transaction_log_index: Some(0.into()), + log_type: None, + removed: None, + }), + None, + )) +} diff --git a/tests/tests/runner.rs b/tests/tests/runner.rs index a5385a81136..52b422da03b 100644 --- a/tests/tests/runner.rs +++ b/tests/tests/runner.rs @@ -3,6 +3,7 @@ use std::sync::atomic::{self, AtomicBool}; use std::sync::Arc; use std::time::Duration; +use assert_json_diff::assert_json_eq; use graph::blockchain::block_stream::BlockWithTriggers; use graph::blockchain::{Block, BlockPtr, Blockchain}; use graph::data::subgraph::schema::{SubgraphError, SubgraphHealth}; @@ -11,8 +12,10 @@ use graph::env::EnvVars; use graph::object; use graph::prelude::ethabi::ethereum_types::H256; use graph::prelude::{CheapClone, SubgraphAssignmentProvider, SubgraphName, SubgraphStore}; -use graph_tests::fixture::ethereum::{chain, empty_block, genesis}; -use graph_tests::fixture::{self, stores, test_ptr, MockAdapterSelector, NoopAdapterSelector}; +use graph_tests::fixture::ethereum::{chain, empty_block, genesis, push_test_log}; +use graph_tests::fixture::{ + self, stores, test_ptr, test_ptr_reorged, MockAdapterSelector, NoopAdapterSelector, +}; use slog::{o, Discard, Logger}; #[tokio::test] @@ -39,16 +42,8 @@ async fn data_source_revert() -> anyhow::Result<()> { vec![block0, block1, block1_reorged, block2, block3, block4] }; - let chain = Arc::new(chain(blocks.clone(), &stores, None).await); - let ctx = fixture::setup( - subgraph_name.clone(), - &hash, - &stores, - chain.clone(), - None, - None, - ) - .await; + let chain = chain(blocks.clone(), &stores, None).await; + let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, &chain, None, None).await; let stop_block = test_ptr(2); ctx.start_and_sync_to(stop_block).await; @@ -70,7 +65,7 @@ async fn data_source_revert() -> anyhow::Result<()> { subgraph_name.clone(), &hash, &stores, - chain, + &chain, graft_block, None, ) @@ -119,8 +114,8 @@ async fn typename() -> anyhow::Result<()> { let stop_block = blocks.last().unwrap().block.ptr(); let stores = stores("./integration-tests/config.simple.toml").await; - let chain = Arc::new(chain(blocks, &stores, None).await); - let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, None, None).await; + let chain = chain(blocks, &stores, None).await; + let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, &chain, None, None).await; ctx.start_and_sync_to(stop_block).await; @@ -143,10 +138,10 @@ async fn file_data_sources() { let block_2 = empty_block(block_1.ptr(), test_ptr(2)); let block_3 = empty_block(block_2.ptr(), test_ptr(3)); let block_4 = empty_block(block_3.ptr(), test_ptr(4)); - let block_5 = empty_block(block_4.ptr(), test_ptr(5)); + let mut block_5 = empty_block(block_4.ptr(), test_ptr(5)); + push_test_log(&mut block_5, "createFile2"); vec![block_0, block_1, block_2, block_3, block_4, block_5] }; - let stop_block = test_ptr(1); // This test assumes the file data sources will be processed in the same block in which they are // created. But the test might fail due to a race condition if for some reason it takes longer @@ -155,31 +150,28 @@ async fn file_data_sources() { // attempt to ensure the monitor has enough time to fetch the file. let adapter_selector = NoopAdapterSelector { x: PhantomData, - triggers_in_block_sleep: Duration::from_millis(100), + triggers_in_block_sleep: Duration::from_millis(150), }; - let chain = Arc::new(chain(blocks, &stores, Some(Arc::new(adapter_selector))).await); - let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, None, None).await; - ctx.start_and_sync_to(stop_block).await; - - // CID QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ is the file - // `file-data-sources/abis/Contract.abi` after being processed by graph-cli. - let id = "QmVkvoPGi9jvvuxsHDVJDgzPEzagBaWSZRYoRDzU244HjZ"; - + let chain = chain(blocks.clone(), &stores, Some(Arc::new(adapter_selector))).await; + let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, &chain, None, None).await; + ctx.start_and_sync_to(test_ptr(1)).await; + + // CID of `file-data-sources/abis/Contract.abi` after being processed by graph-cli. + let id = "QmQ2REmceVtzawp7yrnxLQXgNNCtFHEnig6fL9aqE1kcWq"; + let content_bytes = ctx.ipfs.cat_all(id, Duration::from_secs(10)).await.unwrap(); + let content = String::from_utf8(content_bytes.into()).unwrap(); let query_res = ctx .query(&format!(r#"{{ ipfsFile(id: "{id}") {{ id, content }} }}"#,)) .await .unwrap(); - assert_eq!( + assert_json_eq!( query_res, - Some(object! { ipfsFile: object!{ id: id.clone() , content: "[]" } }) + Some(object! { ipfsFile: object!{ id: id.clone() , content: content.clone() } }) ); // assert whether duplicate data sources are created. - ctx.provider.stop(ctx.deployment.clone()).await.unwrap(); - let stop_block = test_ptr(2); - - ctx.start_and_sync_to(stop_block).await; + ctx.start_and_sync_to(test_ptr(2)).await; let store = ctx.store.cheap_clone(); let writable = store @@ -189,24 +181,19 @@ async fn file_data_sources() { let datasources = writable.load_dynamic_data_sources(vec![]).await.unwrap(); assert!(datasources.len() == 1); - ctx.provider.stop(ctx.deployment.clone()).await.unwrap(); - let stop_block = test_ptr(3); - ctx.start_and_sync_to(stop_block).await; + ctx.start_and_sync_to(test_ptr(3)).await; let query_res = ctx .query(&format!(r#"{{ ipfsFile1(id: "{id}") {{ id, content }} }}"#,)) .await .unwrap(); - assert_eq!( + assert_json_eq!( query_res, - Some(object! { ipfsFile1: object!{ id: id , content: "[]" } }) + Some(object! { ipfsFile1: object!{ id: id , content: content } }) ); - ctx.provider.stop(ctx.deployment.clone()).await.unwrap(); - let stop_block = test_ptr(4); - ctx.start_and_sync_to(stop_block).await; - ctx.provider.stop(ctx.deployment.clone()).await.unwrap(); + ctx.start_and_sync_to(test_ptr(4)).await; let writable = ctx .store .clone() @@ -224,17 +211,72 @@ async fn file_data_sources() { } let stop_block = test_ptr(5); - let err = ctx.start_and_sync_to_error(stop_block).await; + let err = ctx.start_and_sync_to_error(stop_block.clone()).await; let message = "entity type `IpfsFile1` is not on the 'entities' list for data source `File2`. \ - Hint: Add `IpfsFile1` to the 'entities' list, which currently is: `IpfsFile`.\twasm backtrace:\t 0: 0x33bf - !src/mapping/handleFile1\t in handler `handleFile1` at block #5 ()".to_string(); + Hint: Add `IpfsFile1` to the 'entities' list, which currently is: `IpfsFile`.\twasm backtrace:\t 0: 0x365d - !src/mapping/handleFile1\t in handler `handleFile1` at block #5 ()".to_string(); let expected_err = SubgraphError { subgraph_id: ctx.deployment.hash.clone(), message, - block_ptr: Some(test_ptr(5)), + block_ptr: Some(stop_block), handler: None, deterministic: false, }; assert_eq!(err, expected_err); + + // Unfail the subgraph to test a conflict between an onchain and offchain entity + { + ctx.rewind(test_ptr(4)); + + // Replace block number 5 with one that contains a different event + let mut blocks = blocks.clone(); + blocks.pop(); + let block_5_1_ptr = test_ptr_reorged(5, 1); + let mut block_5_1 = empty_block(test_ptr(4), block_5_1_ptr.clone()); + push_test_log(&mut block_5_1, "saveConflictingEntity"); + blocks.push(block_5_1); + + chain.set_block_stream(blocks); + + // Errors in the store pipeline can be observed by using the runner directly. + let runner = ctx.runner(block_5_1_ptr.clone()).await; + let err = runner + .run() + .await + .err() + .unwrap_or_else(|| panic!("subgraph ran successfully but an error was expected")); + + let message = + "store error: conflicting key value violates exclusion constraint \"ipfs_file_id_block_range_excl\"" + .to_string(); + assert_eq!(err.to_string(), message); + } + + // Unfail the subgraph to test a conflict between an onchain and offchain entity + { + // Replace block number 5 with one that contains a different event + let mut blocks = blocks.clone(); + blocks.pop(); + let block_5_2_ptr = test_ptr_reorged(5, 2); + let mut block_5_2 = empty_block(test_ptr(4), block_5_2_ptr.clone()); + push_test_log(&mut block_5_2, "createFile1"); + blocks.push(block_5_2); + + chain.set_block_stream(blocks); + + // Errors in the store pipeline can be observed by using the runner directly. + let err = ctx + .runner(block_5_2_ptr.clone()) + .await + .run() + .await + .err() + .unwrap_or_else(|| panic!("subgraph ran successfully but an error was expected")); + + let message = + "store error: conflicting key value violates exclusion constraint \"ipfs_file_1_id_block_range_excl\"" + .to_string(); + assert_eq!(err.to_string(), message); + } } #[tokio::test] @@ -254,7 +296,7 @@ async fn template_static_filters_false_positives() { vec![block_0, block_1, block_2] }; let stop_block = test_ptr(1); - let chain = Arc::new(chain(blocks, &stores, None).await); + let chain = chain(blocks, &stores, None).await; let mut env_vars = EnvVars::default(); env_vars.experimental_static_filters = true; @@ -263,7 +305,7 @@ async fn template_static_filters_false_positives() { subgraph_name.clone(), &hash, &stores, - chain, + &chain, None, Some(env_vars), ) @@ -329,7 +371,7 @@ async fn retry_create_ds() { triggers_in_block_sleep: Duration::ZERO, triggers_in_block, }); - let chain = Arc::new(chain(blocks, &stores, Some(triggers_adapter)).await); + let chain = chain(blocks, &stores, Some(triggers_adapter)).await; let mut env_vars = EnvVars::default(); env_vars.subgraph_error_retry_ceil = Duration::from_secs(1); @@ -338,7 +380,7 @@ async fn retry_create_ds() { subgraph_name.clone(), &hash, &stores, - chain, + &chain, None, Some(env_vars), ) @@ -373,8 +415,8 @@ async fn fatal_error() -> anyhow::Result<()> { let stop_block = blocks.last().unwrap().block.ptr(); let stores = stores("./integration-tests/config.simple.toml").await; - let chain = Arc::new(chain(blocks, &stores, None).await); - let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, chain, None, None).await; + let chain = chain(blocks, &stores, None).await; + let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, &chain, None, None).await; ctx.start_and_sync_to_error(stop_block).await; @@ -387,7 +429,7 @@ async fn fatal_error() -> anyhow::Result<()> { assert!(err.deterministic); // Test that rewind unfails the subgraph. - ctx.store.rewind(ctx.deployment.hash.clone(), test_ptr(1))?; + ctx.rewind(test_ptr(1)); let status = ctx.indexing_status().await; assert!(status.health == SubgraphHealth::Healthy); assert!(status.fatal_error.is_none()); From e406ae06f5a412a9f643b2c3ef816c90e3f4d1bc Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 24 Jan 2023 11:30:58 -0800 Subject: [PATCH 238/253] store: Add a materialized view 'info.chain_sizes' --- .../down.sql | 10 ++++++ .../2023-01-24-192319_chain_size_view/up.sql | 34 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 store/postgres/migrations/2023-01-24-192319_chain_size_view/down.sql create mode 100644 store/postgres/migrations/2023-01-24-192319_chain_size_view/up.sql diff --git a/store/postgres/migrations/2023-01-24-192319_chain_size_view/down.sql b/store/postgres/migrations/2023-01-24-192319_chain_size_view/down.sql new file mode 100644 index 00000000000..027c1afb4f9 --- /dev/null +++ b/store/postgres/migrations/2023-01-24-192319_chain_size_view/down.sql @@ -0,0 +1,10 @@ +-- This file should undo anything in `up.sql` + +drop view if exists info.all_sizes; + +create view info.all_sizes as +select * from info.subgraph_sizes +union all +select * from info.table_sizes; + +drop materialized view if exists info.chain_sizes; diff --git a/store/postgres/migrations/2023-01-24-192319_chain_size_view/up.sql b/store/postgres/migrations/2023-01-24-192319_chain_size_view/up.sql new file mode 100644 index 00000000000..1d45be2359d --- /dev/null +++ b/store/postgres/migrations/2023-01-24-192319_chain_size_view/up.sql @@ -0,0 +1,34 @@ + +drop materialized view if exists info.chain_sizes; + +create materialized view info.chain_sizes as +select *, + pg_size_pretty(total_bytes) as total, + pg_size_pretty(index_bytes) as index, + pg_size_pretty(toast_bytes) as toast, + pg_size_pretty(table_bytes) as table + from ( + select *, + total_bytes-index_bytes-coalesce(toast_bytes,0) AS table_bytes + from ( + select nspname as table_schema, relname as table_name, + 'shared'::text as version, + c.reltuples as row_estimate, + pg_total_relation_size(c.oid) as total_bytes, + pg_indexes_size(c.oid) as index_bytes, + pg_total_relation_size(reltoastrelid) as toast_bytes + from pg_class c + join pg_namespace n on n.oid = c.relnamespace + where relkind = 'r' + and nspname like 'chain%' + ) a +) a with no data; + +drop view if exists info.all_sizes; + +create view info.all_sizes as +select * from info.subgraph_sizes +union all +select * from info.chain_sizes +union all +select * from info.table_sizes; From af4f653a8c04515ce4d79831d74da20fd18ba42f Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Mon, 30 Jan 2023 14:30:21 +0000 Subject: [PATCH 239/253] config: Bump default ipfs timeout to 60 seconds (#4324) It used to be that 30 seconds was sufficient, but now we see ipfs requests taking minutes to find a file in the DHT. So 60 seconds seems like a reasonable step. --- docs/environment-variables.md | 2 +- graph/src/env/mappings.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 310c83e0a5e..56d5bbed183 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -69,7 +69,7 @@ those. ## IPFS - `GRAPH_IPFS_TIMEOUT`: timeout for IPFS, which includes requests for manifest files - and from mappings (in seconds, default is 30). + and from mappings (in seconds, default is 60). - `GRAPH_MAX_IPFS_FILE_BYTES`: maximum size for a file that can be retrieved (in bytes, default is 256 MiB). - `GRAPH_MAX_IPFS_MAP_FILE_SIZE`: maximum size of files that can be processed with `ipfs.map`. When a file is processed through `ipfs.map`, the entities diff --git a/graph/src/env/mappings.rs b/graph/src/env/mappings.rs index 1f2ac18ae8c..bb3ee2c1d30 100644 --- a/graph/src/env/mappings.rs +++ b/graph/src/env/mappings.rs @@ -36,7 +36,7 @@ pub struct EnvVarsMapping { /// The timeout for all IPFS requests. /// /// Set by the environment variable `GRAPH_IPFS_TIMEOUT` (expressed in - /// seconds). The default value is 30s. + /// seconds). The default value is 60s. pub ipfs_timeout: Duration, /// Sets the `ipfs.map` file size limit. /// @@ -105,7 +105,7 @@ pub struct InnerMappingHandlers { max_ipfs_cache_file_size: WithDefaultUsize, #[envconfig(from = "GRAPH_MAX_IPFS_CACHE_SIZE", default = "50")] max_ipfs_cache_size: u64, - #[envconfig(from = "GRAPH_IPFS_TIMEOUT", default = "30")] + #[envconfig(from = "GRAPH_IPFS_TIMEOUT", default = "60")] ipfs_timeout_in_secs: u64, #[envconfig(from = "GRAPH_MAX_IPFS_MAP_FILE_SIZE", default = "")] max_ipfs_map_file_size: WithDefaultUsize, From 7118fd82148c49da88f59043a73a84598be19116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Duchesneau?= Date: Mon, 30 Jan 2023 12:53:07 -0500 Subject: [PATCH 240/253] strip all null bytes from utf8 strings that come from substreams, like it's done on other sources (#4328) --- chain/substreams/src/trigger.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/chain/substreams/src/trigger.rs b/chain/substreams/src/trigger.rs index 237f9e09793..67dc4dc6bd4 100644 --- a/chain/substreams/src/trigger.rs +++ b/chain/substreams/src/trigger.rs @@ -257,7 +257,15 @@ fn decode_value(value: &crate::codec::value::Typed) -> Result Ok(Value::String(new_value.clone())), + Typed::String(new_value) => { + let mut string = new_value.clone(); + + // Strip null characters since they are not accepted by Postgres. + if string.contains('\u{0000}') { + string = string.replace('\u{0000}', ""); + } + Ok(Value::String(string)) + } Typed::Bytes(new_value) => base64::decode(&new_value) .map(|bs| Value::Bytes(Bytes::from(bs.as_ref()))) From 706d2b11b09660738a41a80df410fff85ccbfc57 Mon Sep 17 00:00:00 2001 From: Leonardo Yvens Date: Fri, 3 Feb 2023 15:44:24 +0000 Subject: [PATCH 241/253] improve(metrics): Turn "Flushing logs to Elasticsearch" log into metric (#4333) --- graph/src/log/elastic.rs | 25 ++++++++++++++++--------- graph/src/log/factory.rs | 25 ++++++++++++++++++++++++- node/src/main.rs | 17 +++++++++-------- node/src/manager/commands/run.rs | 2 +- server/http/tests/server.rs | 13 +++++++++---- tests/src/fixture.rs | 2 +- tests/src/fixture/ethereum.rs | 4 ++-- 7 files changed, 62 insertions(+), 26 deletions(-) diff --git a/graph/src/log/elastic.rs b/graph/src/log/elastic.rs index a08ca5384eb..cbc49810beb 100644 --- a/graph/src/log/elastic.rs +++ b/graph/src/log/elastic.rs @@ -8,6 +8,7 @@ use std::time::Duration; use chrono::prelude::{SecondsFormat, Utc}; use futures03::TryFutureExt; use http::header::CONTENT_TYPE; +use prometheus::Counter; use reqwest; use reqwest::Client; use serde::ser::Serializer as SerdeSerializer; @@ -175,15 +176,21 @@ pub struct ElasticDrainConfig { pub struct ElasticDrain { config: ElasticDrainConfig, error_logger: Logger, + logs_sent_counter: Counter, logs: Arc>>, } impl ElasticDrain { /// Creates a new `ElasticDrain`. - pub fn new(config: ElasticDrainConfig, error_logger: Logger) -> Self { + pub fn new( + config: ElasticDrainConfig, + error_logger: Logger, + logs_sent_counter: Counter, + ) -> Self { let drain = ElasticDrain { config, error_logger, + logs_sent_counter, logs: Arc::new(Mutex::new(vec![])), }; drain.periodically_flush_logs(); @@ -192,6 +199,7 @@ impl ElasticDrain { fn periodically_flush_logs(&self) { let flush_logger = self.error_logger.clone(); + let logs_sent_counter = self.logs_sent_counter.clone(); let logs = self.logs.clone(); let config = self.config.clone(); let mut interval = tokio::time::interval(self.config.flush_interval); @@ -203,7 +211,6 @@ impl ElasticDrain { let logs = logs.clone(); let config = config.clone(); - let flush_logger = flush_logger.clone(); let logs_to_send = { let mut logs = logs.lock().unwrap(); let logs_to_send = (*logs).clone(); @@ -217,11 +224,7 @@ impl ElasticDrain { continue; } - debug!( - flush_logger, - "Flushing {} logs to Elasticsearch", - logs_to_send.len() - ); + logs_sent_counter.inc_by(logs_to_send.len() as f64); // The Elasticsearch batch API takes requests with the following format: // ```ignore @@ -382,8 +385,12 @@ impl Drain for ElasticDrain { /// /// Uses `error_logger` to print any Elasticsearch logging errors, /// so they don't go unnoticed. -pub fn elastic_logger(config: ElasticDrainConfig, error_logger: Logger) -> Logger { - let elastic_drain = ElasticDrain::new(config, error_logger).fuse(); +pub fn elastic_logger( + config: ElasticDrainConfig, + error_logger: Logger, + logs_sent_counter: Counter, +) -> Logger { + let elastic_drain = ElasticDrain::new(config, error_logger, logs_sent_counter).fuse(); let async_drain = slog_async::Async::new(elastic_drain) .chan_size(20000) .build() diff --git a/graph/src/log/factory.rs b/graph/src/log/factory.rs index 8565c5624ff..1b126a6995d 100644 --- a/graph/src/log/factory.rs +++ b/graph/src/log/factory.rs @@ -1,5 +1,9 @@ +use std::sync::Arc; + +use prometheus::Counter; use slog::*; +use crate::components::metrics::MetricsRegistry; use crate::components::store::DeploymentLocator; use crate::log::elastic::*; use crate::log::split::*; @@ -20,14 +24,20 @@ pub struct ComponentLoggerConfig { pub struct LoggerFactory { parent: Logger, elastic_config: Option, + metrics_registry: Arc, } impl LoggerFactory { /// Creates a new factory using a parent logger and optional Elasticsearch configuration. - pub fn new(logger: Logger, elastic_config: Option) -> Self { + pub fn new( + logger: Logger, + elastic_config: Option, + metrics_registry: Arc, + ) -> Self { Self { parent: logger, elastic_config, + metrics_registry, } } @@ -36,6 +46,7 @@ impl LoggerFactory { Self { parent, elastic_config: self.elastic_config.clone(), + metrics_registry: self.metrics_registry.clone(), } } @@ -68,6 +79,7 @@ impl LoggerFactory { max_retries: ENV_VARS.elastic_search_max_retries, }, term_logger.clone(), + self.logs_sent_counter(None), ), ) }) @@ -98,9 +110,20 @@ impl LoggerFactory { max_retries: ENV_VARS.elastic_search_max_retries, }, term_logger.clone(), + self.logs_sent_counter(Some(loc.hash.as_str())), ), ) }) .unwrap_or(term_logger) } + + fn logs_sent_counter(&self, deployment: Option<&str>) -> Counter { + self.metrics_registry + .global_deployment_counter( + "graph_elasticsearch_logs_sent", + "Count of logs sent to Elasticsearch endpoint", + deployment.unwrap_or(""), + ) + .unwrap() + } } diff --git a/node/src/main.rs b/node/src/main.rs index 5e81d07d56d..243cb78b0e9 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -191,8 +191,16 @@ async fn main() { client: reqwest::Client::new(), }); + // Set up Prometheus registry + let prometheus_registry = Arc::new(Registry::new()); + let metrics_registry = Arc::new(MetricsRegistry::new( + logger.clone(), + prometheus_registry.clone(), + )); + // Create a component and subgraph logger factory - let logger_factory = LoggerFactory::new(logger.clone(), elastic_config); + let logger_factory = + LoggerFactory::new(logger.clone(), elastic_config, metrics_registry.clone()); // Try to create IPFS clients for each URL specified in `--ipfs` let ipfs_clients: Vec<_> = create_ipfs_clients(&logger, &opt.ipfs); @@ -207,13 +215,6 @@ async fn main() { // Convert the clients into a link resolver. Since we want to get past // possible temporary DNS failures, make the resolver retry let link_resolver = Arc::new(LinkResolver::new(ipfs_clients, env_vars.cheap_clone())); - - // Set up Prometheus registry - let prometheus_registry = Arc::new(Registry::new()); - let metrics_registry = Arc::new(MetricsRegistry::new( - logger.clone(), - prometheus_registry.clone(), - )); let mut metrics_server = PrometheusMetricsServer::new(&logger_factory, prometheus_registry.clone()); diff --git a/node/src/manager/commands/run.rs b/node/src/manager/commands/run.rs index cd62f299776..181978aac16 100644 --- a/node/src/manager/commands/run.rs +++ b/node/src/manager/commands/run.rs @@ -57,7 +57,7 @@ pub async fn run( let env_vars = Arc::new(EnvVars::from_env().unwrap()); let metrics_registry = metrics_ctx.registry.clone(); - let logger_factory = LoggerFactory::new(logger.clone(), None); + let logger_factory = LoggerFactory::new(logger.clone(), None, metrics_ctx.registry.clone()); // FIXME: Hard-coded IPFS config, take it from config file instead? let ipfs_clients: Vec<_> = create_ipfs_clients(&logger, &ipfs_url); diff --git a/server/http/tests/server.rs b/server/http/tests/server.rs index 899a9effb40..589e10d696b 100644 --- a/server/http/tests/server.rs +++ b/server/http/tests/server.rs @@ -89,6 +89,8 @@ impl GraphQlRunner for TestGraphQlRunner { #[cfg(test)] mod test { + use graph_mock::MockMetricsRegistry; + use super::*; lazy_static! { @@ -101,7 +103,7 @@ mod test { runtime .block_on(async { let logger = Logger::root(slog::Discard, o!()); - let logger_factory = LoggerFactory::new(logger, None); + let logger_factory = LoggerFactory::new(logger, None, Arc::new(MockMetricsRegistry::new())); let id = USERS.clone(); let query_runner = Arc::new(TestGraphQlRunner); let node_id = NodeId::new("test").unwrap(); @@ -142,7 +144,8 @@ mod test { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async { let logger = Logger::root(slog::Discard, o!()); - let logger_factory = LoggerFactory::new(logger, None); + let logger_factory = + LoggerFactory::new(logger, None, Arc::new(MockMetricsRegistry::new())); let id = USERS.clone(); let query_runner = Arc::new(TestGraphQlRunner); let node_id = NodeId::new("test").unwrap(); @@ -222,7 +225,8 @@ mod test { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async { let logger = Logger::root(slog::Discard, o!()); - let logger_factory = LoggerFactory::new(logger, None); + let logger_factory = + LoggerFactory::new(logger, None, Arc::new(MockMetricsRegistry::new())); let id = USERS.clone(); let query_runner = Arc::new(TestGraphQlRunner); let node_id = NodeId::new("test").unwrap(); @@ -267,7 +271,8 @@ mod test { let runtime = tokio::runtime::Runtime::new().unwrap(); let _ = runtime.block_on(async { let logger = Logger::root(slog::Discard, o!()); - let logger_factory = LoggerFactory::new(logger, None); + let logger_factory = + LoggerFactory::new(logger, None, Arc::new(MockMetricsRegistry::new())); let id = USERS.clone(); let query_runner = Arc::new(TestGraphQlRunner); let node_id = NodeId::new("test").unwrap(); diff --git a/tests/src/fixture.rs b/tests/src/fixture.rs index 654bea65014..5a489bbdf72 100644 --- a/tests/src/fixture.rs +++ b/tests/src/fixture.rs @@ -377,8 +377,8 @@ pub async fn setup( }); let logger = graph::log::logger(true); - let logger_factory = LoggerFactory::new(logger.clone(), None); let mock_registry: Arc = Arc::new(MockMetricsRegistry::new()); + let logger_factory = LoggerFactory::new(logger.clone(), None, mock_registry.clone()); let node_id = NodeId::new(NODE_ID).unwrap(); // Make sure we're starting from a clean state. diff --git a/tests/src/fixture/ethereum.rs b/tests/src/fixture/ethereum.rs index 3e94385ece5..426fffb8e0f 100644 --- a/tests/src/fixture/ethereum.rs +++ b/tests/src/fixture/ethereum.rs @@ -31,9 +31,9 @@ pub async fn chain( x: PhantomData, })); let logger = graph::log::logger(true); - let logger_factory = LoggerFactory::new(logger.cheap_clone(), None); - let node_id = NodeId::new(NODE_ID).unwrap(); let mock_registry = Arc::new(MockMetricsRegistry::new()); + let logger_factory = LoggerFactory::new(logger.cheap_clone(), None, mock_registry.clone()); + let node_id = NodeId::new(NODE_ID).unwrap(); let chain_store = stores.chain_store.cheap_clone(); From 2ca18d5f38c058d23556dcfdaa121bbe4b86ff9b Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 6 Feb 2023 16:06:26 -0500 Subject: [PATCH 242/253] store(test-store): use C locale for DB (#4340) --- store/test-store/devel/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/store/test-store/devel/docker-compose.yml b/store/test-store/devel/docker-compose.yml index 5a7d12cae76..a42bec3854f 100644 --- a/store/test-store/devel/docker-compose.yml +++ b/store/test-store/devel/docker-compose.yml @@ -15,6 +15,7 @@ services: POSTGRES_USER: graph-node POSTGRES_PASSWORD: let-me-in POSTGRES_DB: graph-node + POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" volumes: - ./data/postgres:/var/lib/postgresql/data - ./initdb.d:/docker-entrypoint-initdb.d From a88ad3976da300987e417467ff7c293bbe75474f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:23:44 +0000 Subject: [PATCH 243/253] build(deps): bump anyhow from 1.0.68 to 1.0.69 (#4344) Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.68 to 1.0.69. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.68...1.0.69) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- store/postgres/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17be6b764e2..deecc05044a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "arc-swap" diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index e2f50668759..caa1cf36bbb 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -28,7 +28,7 @@ serde = "1.0" uuid = { version = "1.1.2", features = ["v4"] } stable-hash_legacy = { version = "0.3.3", package = "stable-hash" } diesel_derives = "1.4.1" -anyhow = "1.0.68" +anyhow = "1.0.69" git-testament = "0.2.4" itertools = "0.10.5" pin-utils = "0.1" diff --git a/tests/Cargo.toml b/tests/Cargo.toml index cdc636c546e..d08b8b895a3 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -28,7 +28,7 @@ assert-json-diff = "2.0.2" [dev-dependencies] bollard = "0.10" -anyhow = "1.0.68" +anyhow = "1.0.69" lazy_static = "1.4.0" tokio-stream = "0.1" serde_yaml = "0.8" From f92065e254ad95bdccfc7a9648c77eaa676e586f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:24:04 +0000 Subject: [PATCH 244/253] build(deps): bump proc-macro2 from 1.0.50 to 1.0.51 (#4343) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.50 to 1.0.51. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.50...1.0.51) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- runtime/derive/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index deecc05044a..ac1bc536a3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3369,9 +3369,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] diff --git a/runtime/derive/Cargo.toml b/runtime/derive/Cargo.toml index eb3f8e4fb02..c78a5441897 100644 --- a/runtime/derive/Cargo.toml +++ b/runtime/derive/Cargo.toml @@ -9,5 +9,5 @@ proc-macro = true [dependencies] syn = { version = "1.0.98", features = ["full"] } quote = "1.0" -proc-macro2 = "1.0.50" +proc-macro2 = "1.0.51" heck = "0.4" From 9a96aab20c9568ef719ca9b161b1f7c74ede1d2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:24:25 +0000 Subject: [PATCH 245/253] build(deps): bump heck from 0.4.0 to 0.4.1 (#4342) Bumps [heck](https://github.com/withoutboats/heck) from 0.4.0 to 0.4.1. - [Release notes](https://github.com/withoutboats/heck/releases) - [Changelog](https://github.com/withoutboats/heck/blob/master/CHANGELOG.md) - [Commits](https://github.com/withoutboats/heck/commits) --- updated-dependencies: - dependency-name: heck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac1bc536a3e..494c8bdb0e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,7 +525,7 @@ version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", @@ -993,7 +993,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8910921b014e2af16298f006de12aa08af894b71f0f49a486ab6d74b17bbed" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro2", "quote", "syn", @@ -1609,7 +1609,7 @@ name = "graph-chain-common" version = "0.29.0" dependencies = [ "anyhow", - "heck 0.4.0", + "heck 0.4.1", "protobuf 3.2.0", "protobuf-parse", ] @@ -1806,7 +1806,7 @@ dependencies = [ name = "graph-runtime-derive" version = "0.29.0" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro2", "quote", "syn", @@ -2087,9 +2087,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -3410,7 +3410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb5320c680de74ba083512704acb90fe00f28f79207286a848e730c45dd73ed6" dependencies = [ "bytes", - "heck 0.4.0", + "heck 0.4.1", "itertools", "lazy_static", "log", From 4eb93d87b08cda46d5278e101145b38365a25aec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:51:47 +0000 Subject: [PATCH 246/253] build(deps): bump tower-test from `b01bb12` to `74881d5` (#4345) Bumps [tower-test](https://github.com/tower-rs/tower) from `b01bb12` to `74881d5`. - [Release notes](https://github.com/tower-rs/tower/releases) - [Commits](https://github.com/tower-rs/tower/compare/b01bb12ddd3178f2e324839caab3316af593dc6d...74881d531141ba0f07b7f58e2a72e3594e5a665c) --- updated-dependencies: - dependency-name: tower-test dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 494c8bdb0e0..f1b1f3facdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4738,7 +4738,7 @@ dependencies = [ [[package]] name = "tower" version = "0.4.12" -source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" +source = "git+https://github.com/tower-rs/tower.git#74881d531141ba0f07b7f58e2a72e3594e5a665c" dependencies = [ "futures-core", "futures-util", @@ -4796,7 +4796,7 @@ dependencies = [ [[package]] name = "tower-layer" version = "0.3.1" -source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" +source = "git+https://github.com/tower-rs/tower.git#74881d531141ba0f07b7f58e2a72e3594e5a665c" [[package]] name = "tower-layer" @@ -4813,12 +4813,12 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tower-service" version = "0.3.1" -source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" +source = "git+https://github.com/tower-rs/tower.git#74881d531141ba0f07b7f58e2a72e3594e5a665c" [[package]] name = "tower-test" version = "0.4.0" -source = "git+https://github.com/tower-rs/tower.git#b01bb12ddd3178f2e324839caab3316af593dc6d" +source = "git+https://github.com/tower-rs/tower.git#74881d531141ba0f07b7f58e2a72e3594e5a665c" dependencies = [ "futures-util", "pin-project-lite", From 22e94d12120018accfed8661b42ac38861717b50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:11:36 +0000 Subject: [PATCH 247/253] build(deps): bump toml from 0.5.8 to 0.7.1 (#4341) * build(deps): bump toml from 0.5.8 to 0.7.1 Bumps [toml](https://github.com/toml-rs/toml) from 0.5.8 to 0.7.1. - [Release notes](https://github.com/toml-rs/toml/releases) - [Commits](https://github.com/toml-rs/toml/commits/toml-v0.7.1) --- updated-dependencies: - dependency-name: toml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * fix tests --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Filipe Azevedo --- Cargo.lock | 74 +++++++++++++++++++++++++++++++++++++++------- node/Cargo.toml | 2 +- node/src/config.rs | 16 +++++----- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1b1f3facdb..d88ebe491ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1798,7 +1798,7 @@ dependencies = [ "serde_regex", "shellexpand", "termcolor", - "toml", + "toml 0.7.1", "url", ] @@ -2900,6 +2900,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -3328,7 +3337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" dependencies = [ "thiserror", - "toml", + "toml 0.5.11", ] [[package]] @@ -3920,18 +3929,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.127" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.127" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -3968,6 +3977,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.0" @@ -4291,9 +4309,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.98" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -4679,13 +4697,47 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772c1426ab886e7362aedf4abc9c0d1348a979517efedfc25862944d10137af0" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90a238ee2e6ede22fb95350acc78e21dc40da00bb66c0334bde83de4ed89424e" +dependencies = [ + "indexmap", + "nom8", + "serde", + "serde_spanned", + "toml_datetime", +] + [[package]] name = "tonic" version = "0.8.3" @@ -5209,7 +5261,7 @@ dependencies = [ "log", "serde", "sha2 0.9.5", - "toml", + "toml 0.5.11", "winapi", "zstd", ] diff --git a/node/Cargo.toml b/node/Cargo.toml index b2036af11c1..8ff16764655 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -38,7 +38,7 @@ graph-server-metrics = { path = "../server/metrics" } graph-store-postgres = { path = "../store/postgres" } serde = { version = "1.0.126", features = ["derive", "rc"] } serde_regex = "1.1.0" -toml = "0.5.7" +toml = "0.7.1" shellexpand = "2.1.0" termcolor = "1.2.0" diesel = "1.4.8" diff --git a/node/src/config.rs b/node/src/config.rs index 223c8e35754..98704f28e25 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -1228,10 +1228,8 @@ mod tests { ); assert_eq!(true, actual.is_err()); - assert_eq!( - actual.unwrap_err().to_string(), - "missing field `url` at line 1 column 1" - ); + let err_str = actual.unwrap_err().to_string(); + assert_eq!(err_str.contains("missing field `url`"), true, "{}", err_str); } #[test] @@ -1245,9 +1243,12 @@ mod tests { ); assert_eq!(true, actual.is_err()); + let err_str = actual.unwrap_err().to_string(); assert_eq!( - actual.unwrap_err().to_string(), - "missing field `features` at line 1 column 1" + err_str.contains("missing field `features`"), + true, + "{}", + err_str ); } @@ -1318,7 +1319,8 @@ mod tests { ); assert_eq!(true, actual.is_err()); - assert_eq!(actual.unwrap_err().to_string(), "when `details` field is provided, deprecated `url`, `transport`, `features` and `headers` cannot be specified at line 1 column 1"); + let err_str = actual.unwrap_err().to_string(); + assert_eq!(err_str.contains("when `details` field is provided, deprecated `url`, `transport`, `features` and `headers` cannot be specified"),true, "{}", err_str); } #[test] From e4cb769988da3f9a0c00017e672623e8e00463e2 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Wed, 8 Feb 2023 23:09:01 +0000 Subject: [PATCH 248/253] Extend provider load control (match config) to substreams (#4339) - Fixes issue #4338 - Adds rules to ProviderDetails::Firehose - FirehoseEndpoints::random now checks for capacity before returning an endpoint --- chain/ethereum/examples/firehose.rs | 2 + chain/substreams/examples/substreams.rs | 2 + graph/src/firehose/endpoints.rs | 109 ++++++++++++-- node/src/chain.rs | 16 ++- node/src/config.rs | 184 +++++++++++++++++++++++- tests/src/fixture/ethereum.rs | 3 +- 6 files changed, 299 insertions(+), 17 deletions(-) diff --git a/chain/ethereum/examples/firehose.rs b/chain/ethereum/examples/firehose.rs index 385e27d3819..37acba642e3 100644 --- a/chain/ethereum/examples/firehose.rs +++ b/chain/ethereum/examples/firehose.rs @@ -1,6 +1,7 @@ use anyhow::Error; use graph::{ env::env_var, + firehose::SubgraphLimit, prelude::{prost, tokio, tonic}, {firehose, firehose::FirehoseEndpoint}, }; @@ -25,6 +26,7 @@ async fn main() -> Result<(), Error> { token, false, false, + SubgraphLimit::Unlimited, )); loop { diff --git a/chain/substreams/examples/substreams.rs b/chain/substreams/examples/substreams.rs index c07b79518c0..a6f74692f52 100644 --- a/chain/substreams/examples/substreams.rs +++ b/chain/substreams/examples/substreams.rs @@ -1,6 +1,7 @@ use anyhow::{format_err, Context, Error}; use graph::blockchain::block_stream::BlockStreamEvent; use graph::blockchain::substreams_block_stream::SubstreamsBlockStream; +use graph::firehose::SubgraphLimit; use graph::prelude::{info, tokio, DeploymentHash, Registry}; use graph::tokio_stream::StreamExt; use graph::{env::env_var, firehose::FirehoseEndpoint, log::logger, substreams}; @@ -46,6 +47,7 @@ async fn main() -> Result<(), Error> { token, false, false, + SubgraphLimit::Unlimited, )); let mut stream: SubstreamsBlockStream = diff --git a/graph/src/firehose/endpoints.rs b/graph/src/firehose/endpoints.rs index 93b07679ed2..2582df5c119 100644 --- a/graph/src/firehose/endpoints.rs +++ b/graph/src/firehose/endpoints.rs @@ -9,6 +9,7 @@ use crate::{ substreams, }; +use anyhow::bail; use futures03::StreamExt; use http::uri::{Scheme, Uri}; use slog::Logger; @@ -22,7 +23,10 @@ use tonic::{ use super::codec as firehose; -const SUBGRAPHS_PER_CONN: usize = 100; +/// This is constant because we found this magic number of connections after +/// which the grpc connections start to hang. +/// For more details see: https://github.com/graphprotocol/graph-node/issues/3879 +pub const SUBGRAPHS_PER_CONN: usize = 100; #[derive(Clone, Debug)] pub struct FirehoseEndpoint { @@ -30,9 +34,17 @@ pub struct FirehoseEndpoint { pub token: Option, pub filters_enabled: bool, pub compression_enabled: bool, + pub subgraph_limit: usize, channel: Channel, } +#[derive(Clone, Debug)] +pub enum SubgraphLimit { + Unlimited, + Limit(usize), + NoTraffic, +} + impl Display for FirehoseEndpoint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(self.provider.as_str(), f) @@ -46,6 +58,7 @@ impl FirehoseEndpoint { token: Option, filters_enabled: bool, compression_enabled: bool, + subgraph_limit: SubgraphLimit, ) -> Self { let uri = url .as_ref() @@ -78,15 +91,31 @@ impl FirehoseEndpoint { // Timeout on each request, so the timeout to estabilish each 'Blocks' stream. .timeout(Duration::from_secs(120)); + let subgraph_limit = match subgraph_limit { + // See the comment on the constant + SubgraphLimit::Unlimited => SUBGRAPHS_PER_CONN, + // This is checked when parsing from config but doesn't hurt to be defensive. + SubgraphLimit::Limit(limit) => limit.min(SUBGRAPHS_PER_CONN), + SubgraphLimit::NoTraffic => 0, + }; + FirehoseEndpoint { provider: provider.as_ref().to_string(), channel: endpoint.connect_lazy(), token, filters_enabled, compression_enabled, + subgraph_limit, } } + // The SUBGRAPHS_PER_CONN upper bound was already limited so we leave it the same + // we need to use inclusive limits (<=) because there will always be a reference + // inside FirehoseEndpoints that is not used (is always cloned). + pub fn has_subgraph_capacity(self: &Arc) -> bool { + Arc::strong_count(&self) <= self.subgraph_limit + } + pub async fn get_block( &self, cursor: FirehoseCursor, @@ -327,8 +356,8 @@ impl FirehoseEndpoints { .iter() .min_by_key(|x| Arc::strong_count(x)) .ok_or(anyhow!("no available firehose endpoints"))?; - if Arc::strong_count(endpoint) > SUBGRAPHS_PER_CONN { - return Err(anyhow!("all connections saturated with {} connections, increase the firehose conn_pool_size", SUBGRAPHS_PER_CONN)); + if !endpoint.has_subgraph_capacity() { + bail!("all connections saturated with {} connections, increase the firehose conn_pool_size or limit for the node", SUBGRAPHS_PER_CONN); } // Cloning here ensure we have the correct count at any given time, if we return a reference it can be cloned later @@ -396,22 +425,22 @@ impl FirehoseNetworks { #[cfg(test)] mod test { - use std::{mem, str::FromStr, sync::Arc}; + use std::{mem, sync::Arc}; - use http::Uri; - use tonic::transport::Channel; + use crate::firehose::SubgraphLimit; use super::{FirehoseEndpoint, FirehoseEndpoints, SUBGRAPHS_PER_CONN}; #[tokio::test] async fn firehose_endpoint_errors() { - let endpoint = vec![Arc::new(FirehoseEndpoint { - provider: String::new(), - token: None, - filters_enabled: true, - compression_enabled: true, - channel: Channel::builder(Uri::from_str("http://127.0.0.1").unwrap()).connect_lazy(), - })]; + let endpoint = vec![Arc::new(FirehoseEndpoint::new( + String::new(), + "http://127.0.0.1".to_string(), + None, + false, + false, + SubgraphLimit::Unlimited, + ))]; let mut endpoints = FirehoseEndpoints::from(endpoint); @@ -432,4 +461,58 @@ mod test { let err = endpoints.random().unwrap_err(); assert!(err.to_string().contains("no available firehose endpoints")); } + + #[tokio::test] + async fn firehose_endpoint_with_limit() { + let endpoint = vec![Arc::new(FirehoseEndpoint::new( + String::new(), + "http://127.0.0.1".to_string(), + None, + false, + false, + SubgraphLimit::Limit(2), + ))]; + + let mut endpoints = FirehoseEndpoints::from(endpoint); + + let mut keep = vec![]; + for _ in 0..2 { + keep.push(endpoints.random().unwrap()); + } + + let err = endpoints.random().unwrap_err(); + assert!(err.to_string().contains("conn_pool_size")); + + mem::drop(keep); + endpoints.random().unwrap(); + + // Fails when empty too + endpoints.remove(""); + + let err = endpoints.random().unwrap_err(); + assert!(err.to_string().contains("no available firehose endpoints")); + } + + #[tokio::test] + async fn firehose_endpoint_no_traffic() { + let endpoint = vec![Arc::new(FirehoseEndpoint::new( + String::new(), + "http://127.0.0.1".to_string(), + None, + false, + false, + SubgraphLimit::NoTraffic, + ))]; + + let mut endpoints = FirehoseEndpoints::from(endpoint); + + let err = endpoints.random().unwrap_err(); + assert!(err.to_string().contains("conn_pool_size")); + + // Fails when empty too + endpoints.remove(""); + + let err = endpoints.random().unwrap_err(); + assert!(err.to_string().contains("no available firehose endpoints")); + } } diff --git a/node/src/chain.rs b/node/src/chain.rs index 6d17fd385ed..2f4891380b3 100644 --- a/node/src/chain.rs +++ b/node/src/chain.rs @@ -5,7 +5,7 @@ use futures::TryFutureExt; use graph::anyhow::Error; use graph::blockchain::{Block as BlockchainBlock, BlockchainKind, ChainIdentifier}; use graph::cheap_clone::CheapClone; -use graph::firehose::{FirehoseEndpoint, FirehoseNetworks}; +use graph::firehose::{FirehoseEndpoint, FirehoseNetworks, SubgraphLimit}; use graph::ipfs_client::IpfsClient; use graph::prelude::{anyhow, tokio}; use graph::prelude::{prost, MetricsRegistry as MetricsRegistryTrait}; @@ -137,6 +137,7 @@ pub fn create_substreams_networks( firehose.token.clone(), firehose.filters_enabled(), firehose.compression_enabled(), + SubgraphLimit::Unlimited, )), ); } @@ -168,10 +169,22 @@ pub fn create_firehose_networks( "Configuring firehose endpoint"; "provider" => &provider.label, ); + let subgraph_limit = match firehose.limit_for(&config.node) { + Some(limit) if limit == 0 => SubgraphLimit::Unlimited, + Some(limit) => SubgraphLimit::Limit(limit), + None => SubgraphLimit::NoTraffic, + }; let parsed_networks = networks_by_kind .entry(chain.protocol) .or_insert_with(|| FirehoseNetworks::new()); + + // Create n FirehoseEndpoints where n is the size of the pool. If a + // subgraph limit is defined for this endpoint then each endpoint + // instance will have their own subgraph limit. + // eg: pool_size = 3 and sg_limit 2 will result in 3 separate instances + // of FirehoseEndpoint and each of those instance can be used in 2 different + // SubgraphInstances. for i in 0..firehose.conn_pool_size { parsed_networks.insert( name.to_string(), @@ -181,6 +194,7 @@ pub fn create_firehose_networks( firehose.token.clone(), firehose.filters_enabled(), firehose.compression_enabled(), + subgraph_limit.clone(), )), ); } diff --git a/node/src/config.rs b/node/src/config.rs index 98704f28e25..a10260f3b31 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -1,6 +1,7 @@ use graph::{ anyhow::Error, blockchain::BlockchainKind, + firehose::SUBGRAPHS_PER_CONN, prelude::{ anyhow::{anyhow, bail, Context, Result}, info, @@ -566,9 +567,14 @@ pub struct FirehoseProvider { pub conn_pool_size: u16, #[serde(default)] pub features: BTreeSet, + #[serde(default, rename = "match")] + rules: Vec, } impl FirehoseProvider { + pub fn limit_for(&self, node: &NodeId) -> Option { + self.rules.iter().find_map(|r| r.limit_for(node)) + } pub fn filters_enabled(&self) -> bool { self.features.contains(FIREHOSE_FILTER_FEATURE) } @@ -672,6 +678,13 @@ impl Provider { FIREHOSE_PROVIDER_FEATURES )); } + + if firehose.rules.iter().any(|r| r.limit > SUBGRAPHS_PER_CONN) { + bail!( + "per node subgraph limit for firehose/substreams has to be in the range 0-{}", + SUBGRAPHS_PER_CONN + ); + } } ProviderDetails::Web3(ref mut web3) => { @@ -779,7 +792,7 @@ impl<'de> Deserialize<'de> for Provider { let label = label.ok_or_else(|| serde::de::Error::missing_field("label"))?; let details = match details { - Some(v) => { + Some(mut v) => { if url.is_some() || transport.is_some() || features.is_some() @@ -788,6 +801,14 @@ impl<'de> Deserialize<'de> for Provider { return Err(serde::de::Error::custom("when `details` field is provided, deprecated `url`, `transport`, `features` and `headers` cannot be specified")); } + match v { + ProviderDetails::Firehose(ref mut firehose) + | ProviderDetails::Substreams(ref mut firehose) => { + firehose.rules = nodes + } + _ => {} + } + v } None => ProviderDetails::Web3(Web3Provider { @@ -1097,10 +1118,13 @@ where #[cfg(test)] mod tests { + use crate::config::Web3Rule; + use super::{ Chain, Config, FirehoseProvider, Provider, ProviderDetails, Transport, Web3Provider, }; use graph::blockchain::BlockchainKind; + use graph::prelude::regex::Regex; use graph::prelude::NodeId; use http::{HeaderMap, HeaderValue}; use std::collections::BTreeSet; @@ -1341,6 +1365,7 @@ mod tests { token: None, features: BTreeSet::new(), conn_pool_size: 20, + rules: vec![], }), }, actual @@ -1365,6 +1390,7 @@ mod tests { token: None, features: BTreeSet::new(), conn_pool_size: 20, + rules: vec![], }), }, actual @@ -1372,7 +1398,7 @@ mod tests { } #[test] fn it_works_on_new_firehose_provider_from_toml_no_features() { - let actual = toml::from_str( + let mut actual = toml::from_str( r#" label = "firehose" details = { type = "firehose", url = "http://localhost:9000" } @@ -1388,10 +1414,164 @@ mod tests { token: None, features: BTreeSet::new(), conn_pool_size: 20, + rules: vec![], + }), + }, + actual + ); + assert! {actual.validate().is_ok()}; + } + + #[test] + fn it_works_on_new_firehose_provider_with_doc_example_match() { + let mut actual = toml::from_str( + r#" + label = "firehose" + details = { type = "firehose", url = "http://localhost:9000" } + match = [ + { name = "some_node_.*", limit = 10 }, + { name = "other_node_.*", limit = 0 } ] + "#, + ) + .unwrap(); + + assert_eq!( + Provider { + label: "firehose".to_owned(), + details: ProviderDetails::Firehose(FirehoseProvider { + url: "http://localhost:9000".to_owned(), + token: None, + features: BTreeSet::new(), + conn_pool_size: 20, + rules: vec![ + Web3Rule { + name: Regex::new("some_node_.*").unwrap(), + limit: 10, + }, + Web3Rule { + name: Regex::new("other_node_.*").unwrap(), + limit: 0, + } + ], + }), + }, + actual + ); + assert! { actual.validate().is_ok()}; + } + + #[test] + fn it_errors_on_firehose_provider_with_high_limit() { + let mut actual = toml::from_str( + r#" + label = "substreams" + details = { type = "substreams", url = "http://localhost:9000" } + match = [ + { name = "some_node_.*", limit = 101 }, + { name = "other_node_.*", limit = 0 } ] + "#, + ) + .unwrap(); + + assert_eq!( + Provider { + label: "substreams".to_owned(), + details: ProviderDetails::Substreams(FirehoseProvider { + url: "http://localhost:9000".to_owned(), + token: None, + features: BTreeSet::new(), + conn_pool_size: 20, + rules: vec![ + Web3Rule { + name: Regex::new("some_node_.*").unwrap(), + limit: 101, + }, + Web3Rule { + name: Regex::new("other_node_.*").unwrap(), + limit: 0, + } + ], + }), + }, + actual + ); + assert! { actual.validate().is_err()}; + } + + #[test] + fn it_works_on_new_substreams_provider_with_doc_example_match() { + let mut actual = toml::from_str( + r#" + label = "substreams" + details = { type = "substreams", url = "http://localhost:9000" } + match = [ + { name = "some_node_.*", limit = 10 }, + { name = "other_node_.*", limit = 0 } ] + "#, + ) + .unwrap(); + + assert_eq!( + Provider { + label: "substreams".to_owned(), + details: ProviderDetails::Substreams(FirehoseProvider { + url: "http://localhost:9000".to_owned(), + token: None, + features: BTreeSet::new(), + conn_pool_size: 20, + rules: vec![ + Web3Rule { + name: Regex::new("some_node_.*").unwrap(), + limit: 10, + }, + Web3Rule { + name: Regex::new("other_node_.*").unwrap(), + limit: 0, + } + ], + }), + }, + actual + ); + assert! { actual.validate().is_ok()}; + } + + #[test] + fn it_errors_on_substreams_provider_with_high_limit() { + let mut actual = toml::from_str( + r#" + label = "substreams" + details = { type = "substreams", url = "http://localhost:9000" } + match = [ + { name = "some_node_.*", limit = 101 }, + { name = "other_node_.*", limit = 0 } ] + "#, + ) + .unwrap(); + + assert_eq!( + Provider { + label: "substreams".to_owned(), + details: ProviderDetails::Substreams(FirehoseProvider { + url: "http://localhost:9000".to_owned(), + token: None, + features: BTreeSet::new(), + conn_pool_size: 20, + rules: vec![ + Web3Rule { + name: Regex::new("some_node_.*").unwrap(), + limit: 101, + }, + Web3Rule { + name: Regex::new("other_node_.*").unwrap(), + limit: 0, + } + ], }), }, actual ); + assert! { actual.validate().is_err()}; } #[test] diff --git a/tests/src/fixture/ethereum.rs b/tests/src/fixture/ethereum.rs index 426fffb8e0f..5160c3c405a 100644 --- a/tests/src/fixture/ethereum.rs +++ b/tests/src/fixture/ethereum.rs @@ -8,7 +8,7 @@ use super::{ }; use graph::blockchain::{BlockPtr, TriggersAdapterSelector}; use graph::cheap_clone::CheapClone; -use graph::firehose::{FirehoseEndpoint, FirehoseEndpoints}; +use graph::firehose::{FirehoseEndpoint, FirehoseEndpoints, SubgraphLimit}; use graph::prelude::ethabi::ethereum_types::H256; use graph::prelude::web3::types::{Address, Log, Transaction, H160}; use graph::prelude::{ethabi, tiny_keccak, LightEthereumBlock, LoggerFactory, NodeId}; @@ -45,6 +45,7 @@ pub async fn chain( None, true, false, + SubgraphLimit::Unlimited, ))] .into(); From 073790875871d70983312fab7331ec89b00a9a6c Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Thu, 9 Feb 2023 13:16:54 +0000 Subject: [PATCH 249/253] Add support for Web3Call adapter (#4351) - Always prefer web3call adapter for runtime adapter on ethereum if available - Prevent non call functions on call_only adapters - Add web3call type configuration - Require Web3Call endpoint to be archive --- Cargo.lock | 1 + chain/ethereum/Cargo.toml | 1 + chain/ethereum/src/chain.rs | 3 +- chain/ethereum/src/ethereum_adapter.rs | 12 ++ chain/ethereum/src/network.rs | 167 +++++++++++++++++- chain/ethereum/src/runtime/runtime_adapter.rs | 11 +- node/resources/tests/full_config.toml | 1 + node/src/chain.rs | 77 ++++---- node/src/config.rs | 28 ++- tests/src/fixture/ethereum.rs | 5 +- 10 files changed, 258 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d88ebe491ff..e86f01eebb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1640,6 +1640,7 @@ dependencies = [ "envconfig", "futures 0.1.31", "graph", + "graph-mock", "graph-runtime-derive", "graph-runtime-wasm", "hex", diff --git a/chain/ethereum/Cargo.toml b/chain/ethereum/Cargo.toml index 5d813d0b825..4a5a1180dcd 100644 --- a/chain/ethereum/Cargo.toml +++ b/chain/ethereum/Cargo.toml @@ -27,6 +27,7 @@ graph-runtime-derive = { path = "../../runtime/derive" } [dev-dependencies] test-store = { path = "../../store/test-store" } base64 = "0.20.0" +graph-mock = { path = "../../mock" } [build-dependencies] tonic-build = { workspace = true } diff --git a/chain/ethereum/src/chain.rs b/chain/ethereum/src/chain.rs index 1fdaeebbc45..1a5d1d973ef 100644 --- a/chain/ethereum/src/chain.rs +++ b/chain/ethereum/src/chain.rs @@ -176,7 +176,8 @@ impl TriggersAdapterSelector for EthereumAdapterSelector { traces: false, }; - self.adapters.cheapest_with(&adjusted_capabilities)? + self.adapters + .call_or_cheapest(Some(&adjusted_capabilities))? } else { self.adapters.cheapest_with(capabilities)? }; diff --git a/chain/ethereum/src/ethereum_adapter.rs b/chain/ethereum/src/ethereum_adapter.rs index b39b55c068e..a7c0eec5bfb 100644 --- a/chain/ethereum/src/ethereum_adapter.rs +++ b/chain/ethereum/src/ethereum_adapter.rs @@ -63,6 +63,7 @@ pub struct EthereumAdapter { web3: Arc>, metrics: Arc, supports_eip_1898: bool, + call_only: bool, } /// Gas limit for `eth_call`. The value of 50_000_000 is a protocol-wide parameter so this @@ -84,11 +85,16 @@ impl CheapClone for EthereumAdapter { web3: self.web3.cheap_clone(), metrics: self.metrics.cheap_clone(), supports_eip_1898: self.supports_eip_1898, + call_only: self.call_only, } } } impl EthereumAdapter { + pub fn is_call_only(&self) -> bool { + self.call_only + } + pub async fn new( logger: Logger, provider: String, @@ -96,6 +102,7 @@ impl EthereumAdapter { transport: Transport, provider_metrics: Arc, supports_eip_1898: bool, + call_only: bool, ) -> Self { // Unwrap: The transport was constructed with this url, so it is valid and has a host. let hostname = graph::url::Url::parse(url) @@ -122,6 +129,7 @@ impl EthereumAdapter { web3, metrics: provider_metrics, supports_eip_1898: supports_eip_1898 && !is_ganache, + call_only, } } @@ -133,6 +141,8 @@ impl EthereumAdapter { to: BlockNumber, addresses: Vec, ) -> Result, Error> { + assert!(!self.call_only); + let eth = self.clone(); let retry_log_message = format!("trace_filter RPC call for block range: [{}..{}]", from, to); @@ -225,6 +235,8 @@ impl EthereumAdapter { filter: Arc, too_many_logs_fingerprints: &'static [&'static str], ) -> Result, TimeoutError> { + assert!(!self.call_only); + let eth_adapter = self.clone(); let retry_log_message = format!("eth_getLogs RPC call for block range: [{}..{}]", from, to); retry(retry_log_message, &logger) diff --git a/chain/ethereum/src/network.rs b/chain/ethereum/src/network.rs index 7c618659bd9..c36a6ce7ba1 100644 --- a/chain/ethereum/src/network.rs +++ b/chain/ethereum/src/network.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context}; +use anyhow::{anyhow, bail, Context}; use graph::cheap_clone::CheapClone; use graph::prelude::rand::{self, seq::IteratorRandom}; use std::cmp::Ordering; @@ -23,12 +23,26 @@ pub struct EthereumNetworkAdapter { limit: usize, } -#[derive(Clone)] +impl EthereumNetworkAdapter { + fn is_call_only(&self) -> bool { + self.adapter.is_call_only() + } +} + +#[derive(Clone, Default)] pub struct EthereumNetworkAdapters { pub adapters: Vec, + pub call_only_adapters: Vec, } impl EthereumNetworkAdapters { + pub fn push_adapter(&mut self, adapter: EthereumNetworkAdapter) { + if adapter.is_call_only() { + self.call_only_adapters.push(adapter); + } else { + self.adapters.push(adapter); + } + } pub fn all_cheapest_with( &self, required_capabilities: &NodeCapabilities, @@ -73,6 +87,42 @@ impl EthereumNetworkAdapters { self.adapters .retain(|adapter| adapter.adapter.provider() != provider); } + + pub fn call_or_cheapest( + &self, + capabilities: Option<&NodeCapabilities>, + ) -> anyhow::Result> { + match self.call_only_adapter()? { + Some(adapter) => Ok(adapter), + None => self.cheapest_with(capabilities.unwrap_or(&NodeCapabilities { + // Archive is required for call_only + archive: true, + traces: false, + })), + } + } + + pub fn call_only_adapter(&self) -> anyhow::Result>> { + if self.call_only_adapters.is_empty() { + return Ok(None); + } + + let adapters = self + .call_only_adapters + .iter() + .min_by_key(|x| Arc::strong_count(&x.adapter)) + .ok_or(anyhow!("no available call only endpoints"))?; + + // TODO: This will probably blow up a lot sooner than [limit] amount of + // subgraphs, since we probably use a few instances. + if Arc::strong_count(&adapters.adapter) >= adapters.limit { + bail!("call only adapter has reached the concurrency limit"); + } + + // Cloning here ensure we have the correct count at any given time, if we return a reference it can be cloned later + // which could cause a high number of endpoints to be given away before accounting for them. + Ok(Some(adapters.adapter.clone())) + } } #[derive(Clone)] @@ -97,8 +147,9 @@ impl EthereumNetworks { let network_adapters = self .networks .entry(name) - .or_insert(EthereumNetworkAdapters { adapters: vec![] }); - network_adapters.adapters.push(EthereumNetworkAdapter { + .or_insert(EthereumNetworkAdapters::default()); + + network_adapters.push_adapter(EthereumNetworkAdapter { capabilities, adapter, limit, @@ -160,6 +211,14 @@ impl EthereumNetworks { #[cfg(test)] mod tests { + use std::sync::Arc; + + use graph::{prelude::MetricsRegistry, tokio, url::Url}; + use graph_mock::MockMetricsRegistry; + use http::HeaderMap; + + use crate::{EthereumAdapter, EthereumNetworks, ProviderEthRpcMetrics, Transport}; + use super::NodeCapabilities; #[test] @@ -216,4 +275,104 @@ mod tests { assert_eq!(true, &full_traces >= &full); assert_eq!(true, &full_traces >= &full_traces); } + + #[tokio::test] + async fn adapter_selector_selects_eth_call() { + let chain = "mainnet".to_string(); + let logger = graph::log::logger(true); + let mock_registry: Arc = Arc::new(MockMetricsRegistry::new()); + let transport = + Transport::new_rpc(Url::parse("http://127.0.0.1").unwrap(), HeaderMap::new()); + let provider_metrics = Arc::new(ProviderEthRpcMetrics::new(mock_registry.clone())); + + let eth_call_adapter = Arc::new( + EthereumAdapter::new( + logger.clone(), + String::new(), + "http://127.0.0.1", + transport.clone(), + provider_metrics.clone(), + true, + true, + ) + .await, + ); + + let eth_adapter = Arc::new( + EthereumAdapter::new( + logger.clone(), + String::new(), + "http://127.0.0.1", + transport.clone(), + provider_metrics.clone(), + true, + false, + ) + .await, + ); + + let mut adapters = { + let mut ethereum_networks = EthereumNetworks::new(); + ethereum_networks.insert( + chain.clone(), + NodeCapabilities { + archive: true, + traces: false, + }, + eth_call_adapter.clone(), + 3, + ); + ethereum_networks.insert( + chain.clone(), + NodeCapabilities { + archive: true, + traces: false, + }, + eth_adapter.clone(), + 3, + ); + ethereum_networks.networks.get(&chain).unwrap().clone() + }; + // one reference above and one inside adapters struct + assert_eq!(Arc::strong_count(ð_call_adapter), 2); + assert_eq!(Arc::strong_count(ð_adapter), 2); + + { + // Not Found + assert!(adapters + .cheapest_with(&NodeCapabilities { + archive: false, + traces: true, + }) + .is_err()); + + // Check cheapest is not call only + let adapter = adapters + .cheapest_with(&NodeCapabilities { + archive: true, + traces: false, + }) + .unwrap(); + assert_eq!(adapter.is_call_only(), false); + } + + // Check limits + { + let adapter = adapters.call_or_cheapest(None).unwrap(); + assert!(adapter.is_call_only()); + assert!(adapters.call_or_cheapest(None).is_err()); + } + + // Check empty falls back to call only + { + adapters.call_only_adapters = vec![]; + let adapter = adapters + .call_or_cheapest(Some(&NodeCapabilities { + archive: true, + traces: false, + })) + .unwrap(); + assert_eq!(adapter.is_call_only(), false); + } + } } diff --git a/chain/ethereum/src/runtime/runtime_adapter.rs b/chain/ethereum/src/runtime/runtime_adapter.rs index e3f4a17198a..f5cac662cde 100644 --- a/chain/ethereum/src/runtime/runtime_adapter.rs +++ b/chain/ethereum/src/runtime/runtime_adapter.rs @@ -43,13 +43,10 @@ impl blockchain::RuntimeAdapter for RuntimeAdapter { fn host_fns(&self, ds: &DataSource) -> Result, Error> { let abis = ds.mapping.abis.clone(); let call_cache = self.call_cache.cheap_clone(); - let eth_adapter = self - .eth_adapters - .cheapest_with(&NodeCapabilities { - archive: ds.mapping.requires_archive()?, - traces: false, - })? - .cheap_clone(); + let eth_adapter = self.eth_adapters.call_or_cheapest(Some(&NodeCapabilities { + archive: ds.mapping.requires_archive()?, + traces: false, + }))?; let ethereum_call = HostFn { name: "ethereum.call", diff --git a/node/resources/tests/full_config.toml b/node/resources/tests/full_config.toml index 97d3be67856..1f907539194 100644 --- a/node/resources/tests/full_config.toml +++ b/node/resources/tests/full_config.toml @@ -47,6 +47,7 @@ ingestor = "index_0" shard = "primary" provider = [ { label = "mainnet-0", url = "http://rpc.mainnet.io", features = ["archive", "traces"] }, + { label = "mainnet-1", details = { type = "web3call", url = "http://rpc.mainnet.io", features = ["archive", "traces"] }}, { label = "firehose", details = { type = "firehose", url = "http://localhost:9000", features = [] }}, { label = "substreams", details = { type = "substreams", url = "http://localhost:9000", features = [] }}, ] diff --git a/node/src/chain.rs b/node/src/chain.rs index 2f4891380b3..e57ba5d626b 100644 --- a/node/src/chain.rs +++ b/node/src/chain.rs @@ -2,7 +2,7 @@ use crate::config::{Config, ProviderDetails}; use ethereum::{EthereumNetworks, ProviderEthRpcMetrics}; use futures::future::{join_all, try_join_all}; use futures::TryFutureExt; -use graph::anyhow::Error; +use graph::anyhow::{bail, Error}; use graph::blockchain::{Block as BlockchainBlock, BlockchainKind, ChainIdentifier}; use graph::cheap_clone::CheapClone; use graph::firehose::{FirehoseEndpoint, FirehoseNetworks, SubgraphLimit}; @@ -424,44 +424,52 @@ pub async fn create_ethereum_networks_for_chain( .ok_or_else(|| anyhow!("unknown network {}", network_name))?; for provider in &chain.providers { - if let ProviderDetails::Web3(web3) = &provider.details { - let capabilities = web3.node_capabilities(); + let (web3, call_only) = match &provider.details { + ProviderDetails::Web3Call(web3) => (web3, true), + ProviderDetails::Web3(web3) => (web3, false), + _ => continue, + }; - let logger = logger.new(o!("provider" => provider.label.clone())); - info!( - logger, - "Creating transport"; - "url" => &web3.url, - "capabilities" => capabilities - ); + let capabilities = web3.node_capabilities(); + if call_only && !capabilities.archive { + bail!("Ethereum call-only adapters require archive features to be enabled"); + } + + let logger = logger.new(o!("provider" => provider.label.clone())); + info!( + logger, + "Creating transport"; + "url" => &web3.url, + "capabilities" => capabilities + ); - use crate::config::Transport::*; + use crate::config::Transport::*; - let transport = match web3.transport { - Rpc => Transport::new_rpc(Url::parse(&web3.url)?, web3.headers.clone()), - Ipc => Transport::new_ipc(&web3.url).await, - Ws => Transport::new_ws(&web3.url).await, - }; + let transport = match web3.transport { + Rpc => Transport::new_rpc(Url::parse(&web3.url)?, web3.headers.clone()), + Ipc => Transport::new_ipc(&web3.url).await, + Ws => Transport::new_ws(&web3.url).await, + }; - let supports_eip_1898 = !web3.features.contains("no_eip1898"); + let supports_eip_1898 = !web3.features.contains("no_eip1898"); - parsed_networks.insert( - network_name.to_string(), - capabilities, - Arc::new( - graph_chain_ethereum::EthereumAdapter::new( - logger, - provider.label.clone(), - &web3.url, - transport, - eth_rpc_metrics.clone(), - supports_eip_1898, - ) - .await, - ), - web3.limit_for(&config.node), - ); - } + parsed_networks.insert( + network_name.to_string(), + capabilities, + Arc::new( + graph_chain_ethereum::EthereumAdapter::new( + logger, + provider.label.clone(), + &web3.url, + transport, + eth_rpc_metrics.clone(), + supports_eip_1898, + call_only, + ) + .await, + ), + web3.limit_for(&config.node), + ); } parsed_networks.sort(); @@ -523,6 +531,7 @@ mod test { archive: true, traces: false, }; + let has_mainnet_with_traces = ethereum_networks .adapter_with_capabilities("mainnet".to_string(), &traces) .is_ok(); diff --git a/node/src/config.rs b/node/src/config.rs index a10260f3b31..228d3297844 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -548,6 +548,7 @@ pub enum ProviderDetails { Firehose(FirehoseProvider), Web3(Web3Provider), Substreams(FirehoseProvider), + Web3Call(Web3Provider), } const FIREHOSE_FILTER_FEATURE: &str = "filters"; @@ -687,7 +688,7 @@ impl Provider { } } - ProviderDetails::Web3(ref mut web3) => { + ProviderDetails::Web3Call(ref mut web3) | ProviderDetails::Web3(ref mut web3) => { for feature in &web3.features { if !PROVIDER_FEATURES.contains(&feature.as_str()) { return Err(anyhow!( @@ -1623,4 +1624,29 @@ mod tests { read_to_string(&d).expect(&format!("resource {:?} not found", &d)) } + + #[test] + fn it_works_on_web3call_provider_without_transport_from_toml() { + let actual = toml::from_str( + r#" + label = "peering" + details = { type = "web3call", url = "http://localhost:8545", features = [] } + "#, + ) + .unwrap(); + + assert_eq!( + Provider { + label: "peering".to_owned(), + details: ProviderDetails::Web3Call(Web3Provider { + transport: Transport::Rpc, + url: "http://localhost:8545".to_owned(), + features: BTreeSet::new(), + headers: HeaderMap::new(), + rules: Vec::new(), + }), + }, + actual + ); + } } diff --git a/tests/src/fixture/ethereum.rs b/tests/src/fixture/ethereum.rs index 5160c3c405a..bbca9dfe297 100644 --- a/tests/src/fixture/ethereum.rs +++ b/tests/src/fixture/ethereum.rs @@ -60,7 +60,10 @@ pub async fn chain( chain_store.cheap_clone(), chain_store, firehose_endpoints, - EthereumNetworkAdapters { adapters: vec![] }, + EthereumNetworkAdapters { + adapters: vec![], + call_only_adapters: vec![], + }, stores.chain_head_listener.cheap_clone(), block_stream_builder.clone(), Arc::new(StaticBlockRefetcher { x: PhantomData }), From e5dd53df05d0af9ae4e69db2b588f1107dd9f1d6 Mon Sep 17 00:00:00 2001 From: Filippo Neysofu Costa Date: Mon, 13 Feb 2023 12:04:15 +0100 Subject: [PATCH 250/253] tests: refactor integration tests (#4249) * tests: sort alphabetically deps in Cargo.toml * tests: move common/ up a level In preparation to splitting tests/integration-tests/ into 2 directories, we want common/ to be easily accessible from both, so I'm moving it up a level. * tests: new .gitignore * tests: isolate integration and runner tests * tests: refactor and make tests run faster --- .gitignore | 13 +- Cargo.lock | 17 +- tests/Cargo.toml | 16 +- .../common/1_initial_migration.js | 0 .../common/2_deploy_contracts.js | 0 .../common/Migrations.sol | 0 .../common/SimpleContract.sol | 0 .../common/build-contracts.sh | 0 .../api-version-v0-0-4/package.json | 2 +- .../api-version-v0-0-4/truffle.js | 4 +- .../ganache-reverts/package.json | 2 +- .../host-exports/package.json | 2 +- .../integration-tests/host-exports/truffle.js | 4 +- .../non-fatal-errors/package.json | 2 +- .../non-fatal-errors/truffle.js | 4 +- .../package.json | 2 +- tests/integration-tests/package.json | 8 +- .../poi-for-failed-subgraph/package.json | 2 +- .../poi-for-failed-subgraph/truffle.js | 4 +- .../remove-then-update/package.json | 2 +- .../remove-then-update/truffle.js | 4 +- .../value-roundtrip/package.json | 2 +- .../value-roundtrip/truffle.js | 4 +- .../config.simple.toml | 0 .../data-source-revert/abis/Contract.abi | 0 .../data-source-revert/grafted.yaml | 0 .../data-source-revert/package.json | 0 .../data-source-revert/schema.graphql | 0 .../data-source-revert/src/mapping.ts | 0 .../data-source-revert/subgraph.yaml | 0 .../data-source-revert2/abis/Contract.abi | 0 .../data-source-revert2/package.json | 0 .../data-source-revert2/schema.graphql | 0 .../data-source-revert2/src/mapping.ts | 0 .../data-source-revert2/subgraph.yaml | 0 .../dynamic-data-source/abis/Contract.abi | 0 .../dynamic-data-source/package.json | 2 +- .../dynamic-data-source/schema.graphql | 0 .../dynamic-data-source/src/mapping.ts | 0 .../dynamic-data-source/subgraph.yaml | 0 .../fatal-error/abis/Contract.abi | 0 .../fatal-error/package.json | 2 +- .../fatal-error/schema.graphql | 0 .../fatal-error/src/mapping.ts | 0 .../fatal-error/subgraph.yaml | 0 .../file-data-sources/abis/Contract.abi | 0 .../file-data-sources/package.json | 0 .../file-data-sources/schema.graphql | 0 .../file-data-sources/src/mapping.ts | 0 .../file-data-sources/subgraph.yaml | 0 tests/runner-tests/package.json | 11 + .../typename/abis/Contract.abi | 0 .../typename/package.json | 0 .../typename/schema.graphql | 0 .../typename/src/mapping.ts | 0 .../typename/subgraph.yaml | 0 tests/runner-tests/yarn.lock | 12557 ++++++++++++++++ tests/src/docker_utils.rs | 269 + tests/src/{fixture.rs => fixture/mod.rs} | 47 - tests/src/helpers.rs | 115 +- tests/src/lib.rs | 1 + tests/tests/common/docker.rs | 289 - tests/tests/common/mod.rs | 1 - tests/tests/integration_tests.rs | 501 + tests/tests/parallel_tests.rs | 435 - tests/tests/{runner.rs => runner_tests.rs} | 158 +- 66 files changed, 13537 insertions(+), 945 deletions(-) rename tests/{integration-tests => }/common/1_initial_migration.js (100%) rename tests/{integration-tests => }/common/2_deploy_contracts.js (100%) rename tests/{integration-tests => }/common/Migrations.sol (100%) rename tests/{integration-tests => }/common/SimpleContract.sol (100%) rename tests/{integration-tests => }/common/build-contracts.sh (100%) rename tests/{integration-tests => runner-tests}/config.simple.toml (100%) rename tests/{integration-tests => runner-tests}/data-source-revert/abis/Contract.abi (100%) rename tests/{integration-tests => runner-tests}/data-source-revert/grafted.yaml (100%) rename tests/{integration-tests => runner-tests}/data-source-revert/package.json (100%) rename tests/{integration-tests => runner-tests}/data-source-revert/schema.graphql (100%) rename tests/{integration-tests => runner-tests}/data-source-revert/src/mapping.ts (100%) rename tests/{integration-tests => runner-tests}/data-source-revert/subgraph.yaml (100%) rename tests/{integration-tests => runner-tests}/data-source-revert2/abis/Contract.abi (100%) rename tests/{integration-tests => runner-tests}/data-source-revert2/package.json (100%) rename tests/{integration-tests => runner-tests}/data-source-revert2/schema.graphql (100%) rename tests/{integration-tests => runner-tests}/data-source-revert2/src/mapping.ts (100%) rename tests/{integration-tests => runner-tests}/data-source-revert2/subgraph.yaml (100%) rename tests/{integration-tests => runner-tests}/dynamic-data-source/abis/Contract.abi (100%) rename tests/{integration-tests => runner-tests}/dynamic-data-source/package.json (93%) rename tests/{integration-tests => runner-tests}/dynamic-data-source/schema.graphql (100%) rename tests/{integration-tests => runner-tests}/dynamic-data-source/src/mapping.ts (100%) rename tests/{integration-tests => runner-tests}/dynamic-data-source/subgraph.yaml (100%) rename tests/{integration-tests => runner-tests}/fatal-error/abis/Contract.abi (100%) rename tests/{integration-tests => runner-tests}/fatal-error/package.json (99%) rename tests/{integration-tests => runner-tests}/fatal-error/schema.graphql (100%) rename tests/{integration-tests => runner-tests}/fatal-error/src/mapping.ts (100%) rename tests/{integration-tests => runner-tests}/fatal-error/subgraph.yaml (100%) rename tests/{integration-tests => runner-tests}/file-data-sources/abis/Contract.abi (100%) rename tests/{integration-tests => runner-tests}/file-data-sources/package.json (100%) rename tests/{integration-tests => runner-tests}/file-data-sources/schema.graphql (100%) rename tests/{integration-tests => runner-tests}/file-data-sources/src/mapping.ts (100%) rename tests/{integration-tests => runner-tests}/file-data-sources/subgraph.yaml (100%) create mode 100644 tests/runner-tests/package.json rename tests/{integration-tests => runner-tests}/typename/abis/Contract.abi (100%) rename tests/{integration-tests => runner-tests}/typename/package.json (100%) rename tests/{integration-tests => runner-tests}/typename/schema.graphql (100%) rename tests/{integration-tests => runner-tests}/typename/src/mapping.ts (100%) rename tests/{integration-tests => runner-tests}/typename/subgraph.yaml (100%) create mode 100644 tests/runner-tests/yarn.lock create mode 100644 tests/src/docker_utils.rs rename tests/src/{fixture.rs => fixture/mod.rs} (93%) delete mode 100644 tests/tests/common/docker.rs delete mode 100644 tests/tests/common/mod.rs create mode 100644 tests/tests/integration_tests.rs delete mode 100644 tests/tests/parallel_tests.rs rename tests/tests/{runner.rs => runner_tests.rs} (81%) diff --git a/.gitignore b/.gitignore index 7ae6ba30e4d..bf084b6c226 100644 --- a/.gitignore +++ b/.gitignore @@ -20,12 +20,11 @@ lcov.info /docker/parity/network/ **/*/tests/fixtures/ipfs_folder/random.txt -/tests/integration-tests/**/build -/tests/integration-tests/**/generated -/tests/integration-tests/**/node_modules -/tests/integration-tests/**/yarn.lock -/tests/integration-tests/**/yarn-error.log +/tests/**/build +/tests/**/generated +/tests/**/node_modules +/tests/**/yarn-error.log # Built solidity contracts. -/tests/integration-tests/**/bin -/tests/integration-tests/**/truffle_output +/tests/**/bin +/tests/**/truffle_output diff --git a/Cargo.lock b/Cargo.lock index e86f01eebb2..791e9dd5391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1850,7 +1850,7 @@ dependencies = [ "semver", "strum", "strum_macros", - "uuid 1.1.2", + "uuid 1.2.2", "wasm-instrument", "wasmtime", ] @@ -1960,7 +1960,7 @@ dependencies = [ "serde", "stable-hash 0.3.3", "test-store", - "uuid 1.1.2", + "uuid 1.2.2", ] [[package]] @@ -1984,15 +1984,14 @@ dependencies = [ "graph-server-index-node", "graph-store-postgres", "graphql-parser", - "hex", "hyper", "lazy_static", - "port_check", "serde", "serde_yaml", "slog", "tokio", "tokio-stream", + "uuid 1.2.2", ] [[package]] @@ -3209,12 +3208,6 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" -[[package]] -name = "port_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6519412c9e0d4be579b9f0618364d19cb434b324fc6ddb1b27b1e682c7105ed" - [[package]] name = "postgres" version = "0.19.1" @@ -5078,9 +5071,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ "getrandom", ] diff --git a/tests/Cargo.toml b/tests/Cargo.toml index d08b8b895a3..67005b2e14e 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -4,13 +4,13 @@ version.workspace = true edition.workspace = true [dependencies] -port_check = "0.1.5" anyhow = "1.0" +assert-json-diff = "2.0.2" +async-stream = "0.3.3" +bollard = "0.10" futures = { version = "0.3", features = ["compat"] } graph = { path = "../graph" } -tokio = { version = "1.16.1", features = ["rt", "macros", "process"] } graph-chain-ethereum = { path = "../chain/ethereum" } -async-stream = "0.3.3" graph-node = { path = "../node" } graph-core = { path = "../core" } graph-mock = { path = "../mock" } @@ -18,17 +18,17 @@ graph-graphql = { path = "../graphql" } graph-store-postgres = { path = "../store/postgres" } graph-runtime-wasm = { path = "../runtime/wasm" } graph-server-index-node = { path = "../server/index-node" } -slog = { version = "2.7.0", features = ["release_max_level_trace", "max_level_trace"] } graphql-parser = "0.4.0" -hex = "0.4.3" -serde_yaml = "0.8" hyper = "0.14" serde = "1.0" -assert-json-diff = "2.0.2" +serde_yaml = "0.8" +slog = { version = "2.7.0", features = ["release_max_level_trace", "max_level_trace"] } +tokio = { version = "1.16.1", features = ["rt", "macros", "process"] } +uuid = { version = "1.2.2", features = ["v4"] } [dev-dependencies] -bollard = "0.10" anyhow = "1.0.69" +bollard = "0.10" lazy_static = "1.4.0" tokio-stream = "0.1" serde_yaml = "0.8" diff --git a/tests/integration-tests/common/1_initial_migration.js b/tests/common/1_initial_migration.js similarity index 100% rename from tests/integration-tests/common/1_initial_migration.js rename to tests/common/1_initial_migration.js diff --git a/tests/integration-tests/common/2_deploy_contracts.js b/tests/common/2_deploy_contracts.js similarity index 100% rename from tests/integration-tests/common/2_deploy_contracts.js rename to tests/common/2_deploy_contracts.js diff --git a/tests/integration-tests/common/Migrations.sol b/tests/common/Migrations.sol similarity index 100% rename from tests/integration-tests/common/Migrations.sol rename to tests/common/Migrations.sol diff --git a/tests/integration-tests/common/SimpleContract.sol b/tests/common/SimpleContract.sol similarity index 100% rename from tests/integration-tests/common/SimpleContract.sol rename to tests/common/SimpleContract.sol diff --git a/tests/integration-tests/common/build-contracts.sh b/tests/common/build-contracts.sh similarity index 100% rename from tests/integration-tests/common/build-contracts.sh rename to tests/common/build-contracts.sh diff --git a/tests/integration-tests/api-version-v0-0-4/package.json b/tests/integration-tests/api-version-v0-0-4/package.json index 812d3cab9fa..4fae4144853 100644 --- a/tests/integration-tests/api-version-v0-0-4/package.json +++ b/tests/integration-tests/api-version-v0-0-4/package.json @@ -2,7 +2,7 @@ "name": "api-version-v0-0-4", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/api-version-v0-0-4 --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/api-version-v0-0-4/truffle.js b/tests/integration-tests/api-version-v0-0-4/truffle.js index 55e43ccf6a4..58130e7d21d 100644 --- a/tests/integration-tests/api-version-v0-0-4/truffle.js +++ b/tests/integration-tests/api-version-v0-0-4/truffle.js @@ -2,8 +2,8 @@ require("babel-register"); require("babel-polyfill"); module.exports = { - contracts_directory: "../common", - migrations_directory: "../common", + contracts_directory: "../../common", + migrations_directory: "../../common", contracts_build_directory: "./truffle_output", networks: { test: { diff --git a/tests/integration-tests/ganache-reverts/package.json b/tests/integration-tests/ganache-reverts/package.json index 7ff49267d8a..fdc5f9119d5 100644 --- a/tests/integration-tests/ganache-reverts/package.json +++ b/tests/integration-tests/ganache-reverts/package.json @@ -2,7 +2,7 @@ "name": "ganache-reverts", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/ganache-reverts --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/host-exports/package.json b/tests/integration-tests/host-exports/package.json index b4857ab13df..038d6f2cac2 100644 --- a/tests/integration-tests/host-exports/package.json +++ b/tests/integration-tests/host-exports/package.json @@ -2,7 +2,7 @@ "name": "host-exports", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/host-exports --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/host-exports/truffle.js b/tests/integration-tests/host-exports/truffle.js index f8596221165..e3957ea52e8 100644 --- a/tests/integration-tests/host-exports/truffle.js +++ b/tests/integration-tests/host-exports/truffle.js @@ -2,8 +2,8 @@ require("babel-register"); require("babel-polyfill"); module.exports = { - contracts_directory: "../common", - migrations_directory: "../common", + contracts_directory: "../../common", + migrations_directory: "../../common", contracts_build_directory: "./truffle_output", networks: { test: { diff --git a/tests/integration-tests/non-fatal-errors/package.json b/tests/integration-tests/non-fatal-errors/package.json index 3688a498973..162a4dbd213 100644 --- a/tests/integration-tests/non-fatal-errors/package.json +++ b/tests/integration-tests/non-fatal-errors/package.json @@ -2,7 +2,7 @@ "name": "non-fatal-errors", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/non-fatal-errors --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/non-fatal-errors/truffle.js b/tests/integration-tests/non-fatal-errors/truffle.js index f8596221165..e3957ea52e8 100644 --- a/tests/integration-tests/non-fatal-errors/truffle.js +++ b/tests/integration-tests/non-fatal-errors/truffle.js @@ -2,8 +2,8 @@ require("babel-register"); require("babel-polyfill"); module.exports = { - contracts_directory: "../common", - migrations_directory: "../common", + contracts_directory: "../../common", + migrations_directory: "../../common", contracts_build_directory: "./truffle_output", networks: { test: { diff --git a/tests/integration-tests/overloaded-contract-functions/package.json b/tests/integration-tests/overloaded-contract-functions/package.json index 2c7972bf81b..ac6c59b16db 100644 --- a/tests/integration-tests/overloaded-contract-functions/package.json +++ b/tests/integration-tests/overloaded-contract-functions/package.json @@ -2,7 +2,7 @@ "name": "overloaded-contract-functions", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/overloaded-contract-functions --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/package.json b/tests/integration-tests/package.json index 064a2336512..1d272de326c 100644 --- a/tests/integration-tests/package.json +++ b/tests/integration-tests/package.json @@ -2,18 +2,12 @@ "private": true, "workspaces": [ "api-version-v0-0-4", - "data-source-revert", - "data-source-revert2", - "fatal-error", "ganache-reverts", "host-exports", "non-fatal-errors", "overloaded-contract-functions", "poi-for-failed-subgraph", "remove-then-update", - "typename", - "value-roundtrip", - "dynamic-data-source", - "file-data-sources" + "value-roundtrip" ] } diff --git a/tests/integration-tests/poi-for-failed-subgraph/package.json b/tests/integration-tests/poi-for-failed-subgraph/package.json index 13698e41389..665bf0ace52 100644 --- a/tests/integration-tests/poi-for-failed-subgraph/package.json +++ b/tests/integration-tests/poi-for-failed-subgraph/package.json @@ -2,7 +2,7 @@ "name": "poi-for-failed-subgraph", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/poi-for-failed-subgraph --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/poi-for-failed-subgraph/truffle.js b/tests/integration-tests/poi-for-failed-subgraph/truffle.js index 55e43ccf6a4..58130e7d21d 100644 --- a/tests/integration-tests/poi-for-failed-subgraph/truffle.js +++ b/tests/integration-tests/poi-for-failed-subgraph/truffle.js @@ -2,8 +2,8 @@ require("babel-register"); require("babel-polyfill"); module.exports = { - contracts_directory: "../common", - migrations_directory: "../common", + contracts_directory: "../../common", + migrations_directory: "../../common", contracts_build_directory: "./truffle_output", networks: { test: { diff --git a/tests/integration-tests/remove-then-update/package.json b/tests/integration-tests/remove-then-update/package.json index 60254bee30f..91eea8a5417 100644 --- a/tests/integration-tests/remove-then-update/package.json +++ b/tests/integration-tests/remove-then-update/package.json @@ -2,7 +2,7 @@ "name": "remove-then-update", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/remove-then-update --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/remove-then-update/truffle.js b/tests/integration-tests/remove-then-update/truffle.js index f8596221165..e3957ea52e8 100644 --- a/tests/integration-tests/remove-then-update/truffle.js +++ b/tests/integration-tests/remove-then-update/truffle.js @@ -2,8 +2,8 @@ require("babel-register"); require("babel-polyfill"); module.exports = { - contracts_directory: "../common", - migrations_directory: "../common", + contracts_directory: "../../common", + migrations_directory: "../../common", contracts_build_directory: "./truffle_output", networks: { test: { diff --git a/tests/integration-tests/value-roundtrip/package.json b/tests/integration-tests/value-roundtrip/package.json index 7dde72002bc..cf177d5c862 100644 --- a/tests/integration-tests/value-roundtrip/package.json +++ b/tests/integration-tests/value-roundtrip/package.json @@ -2,7 +2,7 @@ "name": "value-roundtrip", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/value-roundtrip --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/value-roundtrip/truffle.js b/tests/integration-tests/value-roundtrip/truffle.js index 83606ff75a1..27a9675b4d7 100644 --- a/tests/integration-tests/value-roundtrip/truffle.js +++ b/tests/integration-tests/value-roundtrip/truffle.js @@ -2,8 +2,8 @@ require("babel-register"); require("babel-polyfill"); module.exports = { - contracts_directory: "../common", - migrations_directory: "../common", + contracts_directory: "../../common", + migrations_directory: "../../common", contracts_build_directory: "./truffle_output", networks: { test: { diff --git a/tests/integration-tests/config.simple.toml b/tests/runner-tests/config.simple.toml similarity index 100% rename from tests/integration-tests/config.simple.toml rename to tests/runner-tests/config.simple.toml diff --git a/tests/integration-tests/data-source-revert/abis/Contract.abi b/tests/runner-tests/data-source-revert/abis/Contract.abi similarity index 100% rename from tests/integration-tests/data-source-revert/abis/Contract.abi rename to tests/runner-tests/data-source-revert/abis/Contract.abi diff --git a/tests/integration-tests/data-source-revert/grafted.yaml b/tests/runner-tests/data-source-revert/grafted.yaml similarity index 100% rename from tests/integration-tests/data-source-revert/grafted.yaml rename to tests/runner-tests/data-source-revert/grafted.yaml diff --git a/tests/integration-tests/data-source-revert/package.json b/tests/runner-tests/data-source-revert/package.json similarity index 100% rename from tests/integration-tests/data-source-revert/package.json rename to tests/runner-tests/data-source-revert/package.json diff --git a/tests/integration-tests/data-source-revert/schema.graphql b/tests/runner-tests/data-source-revert/schema.graphql similarity index 100% rename from tests/integration-tests/data-source-revert/schema.graphql rename to tests/runner-tests/data-source-revert/schema.graphql diff --git a/tests/integration-tests/data-source-revert/src/mapping.ts b/tests/runner-tests/data-source-revert/src/mapping.ts similarity index 100% rename from tests/integration-tests/data-source-revert/src/mapping.ts rename to tests/runner-tests/data-source-revert/src/mapping.ts diff --git a/tests/integration-tests/data-source-revert/subgraph.yaml b/tests/runner-tests/data-source-revert/subgraph.yaml similarity index 100% rename from tests/integration-tests/data-source-revert/subgraph.yaml rename to tests/runner-tests/data-source-revert/subgraph.yaml diff --git a/tests/integration-tests/data-source-revert2/abis/Contract.abi b/tests/runner-tests/data-source-revert2/abis/Contract.abi similarity index 100% rename from tests/integration-tests/data-source-revert2/abis/Contract.abi rename to tests/runner-tests/data-source-revert2/abis/Contract.abi diff --git a/tests/integration-tests/data-source-revert2/package.json b/tests/runner-tests/data-source-revert2/package.json similarity index 100% rename from tests/integration-tests/data-source-revert2/package.json rename to tests/runner-tests/data-source-revert2/package.json diff --git a/tests/integration-tests/data-source-revert2/schema.graphql b/tests/runner-tests/data-source-revert2/schema.graphql similarity index 100% rename from tests/integration-tests/data-source-revert2/schema.graphql rename to tests/runner-tests/data-source-revert2/schema.graphql diff --git a/tests/integration-tests/data-source-revert2/src/mapping.ts b/tests/runner-tests/data-source-revert2/src/mapping.ts similarity index 100% rename from tests/integration-tests/data-source-revert2/src/mapping.ts rename to tests/runner-tests/data-source-revert2/src/mapping.ts diff --git a/tests/integration-tests/data-source-revert2/subgraph.yaml b/tests/runner-tests/data-source-revert2/subgraph.yaml similarity index 100% rename from tests/integration-tests/data-source-revert2/subgraph.yaml rename to tests/runner-tests/data-source-revert2/subgraph.yaml diff --git a/tests/integration-tests/dynamic-data-source/abis/Contract.abi b/tests/runner-tests/dynamic-data-source/abis/Contract.abi similarity index 100% rename from tests/integration-tests/dynamic-data-source/abis/Contract.abi rename to tests/runner-tests/dynamic-data-source/abis/Contract.abi diff --git a/tests/integration-tests/dynamic-data-source/package.json b/tests/runner-tests/dynamic-data-source/package.json similarity index 93% rename from tests/integration-tests/dynamic-data-source/package.json rename to tests/runner-tests/dynamic-data-source/package.json index 44be4b4408d..7e31db5c444 100644 --- a/tests/integration-tests/dynamic-data-source/package.json +++ b/tests/runner-tests/dynamic-data-source/package.json @@ -2,7 +2,7 @@ "name": "dynamic-data-source", "version": "0.1.0", "scripts": { - "build-contracts": "../common/build-contracts.sh", + "build-contracts": "../../common/build-contracts.sh", "codegen": "graph codegen --skip-migrations", "test": "yarn build-contracts && truffle test --compile-none --network test", "create:test": "graph create test/dynamic-data-source --node $GRAPH_NODE_ADMIN_URI", diff --git a/tests/integration-tests/dynamic-data-source/schema.graphql b/tests/runner-tests/dynamic-data-source/schema.graphql similarity index 100% rename from tests/integration-tests/dynamic-data-source/schema.graphql rename to tests/runner-tests/dynamic-data-source/schema.graphql diff --git a/tests/integration-tests/dynamic-data-source/src/mapping.ts b/tests/runner-tests/dynamic-data-source/src/mapping.ts similarity index 100% rename from tests/integration-tests/dynamic-data-source/src/mapping.ts rename to tests/runner-tests/dynamic-data-source/src/mapping.ts diff --git a/tests/integration-tests/dynamic-data-source/subgraph.yaml b/tests/runner-tests/dynamic-data-source/subgraph.yaml similarity index 100% rename from tests/integration-tests/dynamic-data-source/subgraph.yaml rename to tests/runner-tests/dynamic-data-source/subgraph.yaml diff --git a/tests/integration-tests/fatal-error/abis/Contract.abi b/tests/runner-tests/fatal-error/abis/Contract.abi similarity index 100% rename from tests/integration-tests/fatal-error/abis/Contract.abi rename to tests/runner-tests/fatal-error/abis/Contract.abi diff --git a/tests/integration-tests/fatal-error/package.json b/tests/runner-tests/fatal-error/package.json similarity index 99% rename from tests/integration-tests/fatal-error/package.json rename to tests/runner-tests/fatal-error/package.json index 02581974bb9..2df8ce04bf0 100644 --- a/tests/integration-tests/fatal-error/package.json +++ b/tests/runner-tests/fatal-error/package.json @@ -17,4 +17,4 @@ "babel-register": "^6.26.0", "gluegun": "^4.6.1" } -} \ No newline at end of file +} diff --git a/tests/integration-tests/fatal-error/schema.graphql b/tests/runner-tests/fatal-error/schema.graphql similarity index 100% rename from tests/integration-tests/fatal-error/schema.graphql rename to tests/runner-tests/fatal-error/schema.graphql diff --git a/tests/integration-tests/fatal-error/src/mapping.ts b/tests/runner-tests/fatal-error/src/mapping.ts similarity index 100% rename from tests/integration-tests/fatal-error/src/mapping.ts rename to tests/runner-tests/fatal-error/src/mapping.ts diff --git a/tests/integration-tests/fatal-error/subgraph.yaml b/tests/runner-tests/fatal-error/subgraph.yaml similarity index 100% rename from tests/integration-tests/fatal-error/subgraph.yaml rename to tests/runner-tests/fatal-error/subgraph.yaml diff --git a/tests/integration-tests/file-data-sources/abis/Contract.abi b/tests/runner-tests/file-data-sources/abis/Contract.abi similarity index 100% rename from tests/integration-tests/file-data-sources/abis/Contract.abi rename to tests/runner-tests/file-data-sources/abis/Contract.abi diff --git a/tests/integration-tests/file-data-sources/package.json b/tests/runner-tests/file-data-sources/package.json similarity index 100% rename from tests/integration-tests/file-data-sources/package.json rename to tests/runner-tests/file-data-sources/package.json diff --git a/tests/integration-tests/file-data-sources/schema.graphql b/tests/runner-tests/file-data-sources/schema.graphql similarity index 100% rename from tests/integration-tests/file-data-sources/schema.graphql rename to tests/runner-tests/file-data-sources/schema.graphql diff --git a/tests/integration-tests/file-data-sources/src/mapping.ts b/tests/runner-tests/file-data-sources/src/mapping.ts similarity index 100% rename from tests/integration-tests/file-data-sources/src/mapping.ts rename to tests/runner-tests/file-data-sources/src/mapping.ts diff --git a/tests/integration-tests/file-data-sources/subgraph.yaml b/tests/runner-tests/file-data-sources/subgraph.yaml similarity index 100% rename from tests/integration-tests/file-data-sources/subgraph.yaml rename to tests/runner-tests/file-data-sources/subgraph.yaml diff --git a/tests/runner-tests/package.json b/tests/runner-tests/package.json new file mode 100644 index 00000000000..c35ae39ad3d --- /dev/null +++ b/tests/runner-tests/package.json @@ -0,0 +1,11 @@ +{ + "private": true, + "workspaces": [ + "data-source-revert", + "data-source-revert2", + "dynamic-data-source", + "fatal-error", + "file-data-sources", + "typename" + ] +} diff --git a/tests/integration-tests/typename/abis/Contract.abi b/tests/runner-tests/typename/abis/Contract.abi similarity index 100% rename from tests/integration-tests/typename/abis/Contract.abi rename to tests/runner-tests/typename/abis/Contract.abi diff --git a/tests/integration-tests/typename/package.json b/tests/runner-tests/typename/package.json similarity index 100% rename from tests/integration-tests/typename/package.json rename to tests/runner-tests/typename/package.json diff --git a/tests/integration-tests/typename/schema.graphql b/tests/runner-tests/typename/schema.graphql similarity index 100% rename from tests/integration-tests/typename/schema.graphql rename to tests/runner-tests/typename/schema.graphql diff --git a/tests/integration-tests/typename/src/mapping.ts b/tests/runner-tests/typename/src/mapping.ts similarity index 100% rename from tests/integration-tests/typename/src/mapping.ts rename to tests/runner-tests/typename/src/mapping.ts diff --git a/tests/integration-tests/typename/subgraph.yaml b/tests/runner-tests/typename/subgraph.yaml similarity index 100% rename from tests/integration-tests/typename/subgraph.yaml rename to tests/runner-tests/typename/subgraph.yaml diff --git a/tests/runner-tests/yarn.lock b/tests/runner-tests/yarn.lock new file mode 100644 index 00000000000..df7ba6fc49a --- /dev/null +++ b/tests/runner-tests/yarn.lock @@ -0,0 +1,12557 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@apollo/client@^3.1.5": + version "3.3.11" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.3.11.tgz#125051405e83dc899d471d43b79fd6045d92a802" + integrity sha512-54+D5FB6RJlQ+g37f432gaexnyvDsG5X6L9VO5kqN54HJlbF8hCf/8CXtAQEHCWodAwZhy6kOLp2RM96829q3A== + dependencies: + "@graphql-typed-document-node/core" "^3.0.0" + "@types/zen-observable" "^0.8.0" + "@wry/context" "^0.5.2" + "@wry/equality" "^0.3.0" + fast-json-stable-stringify "^2.0.0" + graphql-tag "^2.12.0" + hoist-non-react-statics "^3.3.2" + optimism "^0.14.0" + prop-types "^15.7.2" + symbol-observable "^2.0.0" + ts-invariant "^0.6.0" + tslib "^1.10.0" + zen-observable "^0.8.14" + +"@apollo/protobufjs@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.2.tgz#4bd92cd7701ccaef6d517cdb75af2755f049f87c" + integrity sha512-vF+zxhPiLtkwxONs6YanSt1EpwpGilThpneExUN5K3tCymuxNnVq2yojTvnpRjv2QfsEIt/n7ozPIIzBLwGIDQ== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.0" + "@types/node" "^10.1.0" + long "^4.0.0" + +"@apollographql/apollo-tools@^0.5.0": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.5.2.tgz#01750a655731a198c3634ee819c463254a7c7767" + integrity sha512-KxZiw0Us3k1d0YkJDhOpVH5rJ+mBfjXcgoRoCcslbgirjgLotKMzOcx4PZ7YTEvvEROmvG7X3Aon41GvMmyGsw== + +"@apollographql/graphql-playground-html@1.6.27": + version "1.6.27" + resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.27.tgz#bc9ab60e9445aa2a8813b4e94f152fa72b756335" + integrity sha512-tea2LweZvn6y6xFV11K0KC8ETjmm52mQrW+ezgB2O/aTQf8JGyFmMcRPFgUaQZeHbWdm8iisDC6EjOKsXu0nfw== + dependencies: + xss "^1.0.8" + +"@apollographql/graphql-upload-8-fork@^8.1.3": + version "8.1.3" + resolved "https://registry.yarnpkg.com/@apollographql/graphql-upload-8-fork/-/graphql-upload-8-fork-8.1.3.tgz#a0d4e0d5cec8e126d78bd915c264d6b90f5784bc" + integrity sha512-ssOPUT7euLqDXcdVv3Qs4LoL4BPtfermW1IOouaqEmj36TpHYDmYDIbKoSQxikd9vtMumFnP87OybH7sC9fJ6g== + dependencies: + "@types/express" "*" + "@types/fs-capacitor" "*" + "@types/koa" "*" + busboy "^0.3.1" + fs-capacitor "^2.0.4" + http-errors "^1.7.3" + object-path "^0.11.4" + +"@ardatan/aggregate-error@0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" + integrity sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ== + dependencies: + tslib "~2.0.1" + +"@babel/code-frame@^7.0.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.8.tgz#5b783b9808f15cef71547f1b691f34f8ff6003a6" + integrity sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog== + +"@babel/core@^7.0.0": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" + integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-compilation-targets" "^7.13.10" + "@babel/helper-module-transforms" "^7.13.0" + "@babel/helpers" "^7.13.10" + "@babel/parser" "^7.13.10" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/generator@^7.12.13", "@babel/generator@^7.13.0", "@babel/generator@^7.13.9", "@babel/generator@^7.5.0": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" + integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== + dependencies: + "@babel/types" "^7.13.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" + integrity sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.8": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c" + integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== + dependencies: + "@babel/compat-data" "^7.13.8" + "@babel/helper-validator-option" "^7.12.17" + browserslist "^4.14.5" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.13.0": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.10.tgz#073b2bbb925a097643c6fc5770e5f13394e887c9" + integrity sha512-YV7r2YxdTUaw84EwNkyrRke/TJHR/UXGiyvACRqvdVJ2/syV2rQuJNnaRLSuYiop8cMRXOgseTGoJCWX0q2fFg== + dependencies: + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-member-expression-to-functions" "^7.13.0" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/helper-replace-supers" "^7.13.0" + "@babel/helper-split-export-declaration" "^7.12.13" + +"@babel/helper-define-polyfill-provider@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz#3c2f91b7971b9fc11fe779c945c014065dea340e" + integrity sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-member-expression-to-functions@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz#6aa4bb678e0f8c22f58cdb79451d30494461b091" + integrity sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ== + dependencies: + "@babel/types" "^7.13.0" + +"@babel/helper-module-imports@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0" + integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-module-transforms@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1" + integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw== + dependencies: + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-replace-supers" "^7.13.0" + "@babel/helper-simple-access" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" + integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== + +"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz#6034b7b51943094cb41627848cb219cb02be1d24" + integrity sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.13.0" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + +"@babel/helper-simple-access@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4" + integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + +"@babel/helper-validator-option@^7.12.17": + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" + integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== + +"@babel/helpers@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" + integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== + dependencies: + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + +"@babel/highlight@^7.12.13", "@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@7.12.16": + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.16.tgz#cc31257419d2c3189d394081635703f549fc1ed4" + integrity sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw== + +"@babel/parser@^7.0.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.10.tgz#8f8f9bf7b3afa3eabd061f7a5bcdf4fec3c48409" + integrity sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ== + +"@babel/plugin-proposal-class-properties@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz#146376000b94efd001e57a40a88a525afaab9f37" + integrity sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.13.0" + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/plugin-proposal-object-rest-spread@^7.0.0": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz#5d210a4d727d6ce3b18f9de82cc99a3964eed60a" + integrity sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g== + dependencies: + "@babel/compat-data" "^7.13.8" + "@babel/helper-compilation-targets" "^7.13.8" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.13.0" + +"@babel/plugin-syntax-class-properties@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.13.tgz#5df9962503c0a9c918381c929d51d4d6949e7e86" + integrity sha512-J/RYxnlSLXZLVR7wTRsozxKT8qbsx1mNKJzXEEjQ0Kjx1ZACcyHgbanNWNCFtc36IzuWhYWPpvJFFoexoOWFmA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz#044fb81ebad6698fe62c478875575bcbb9b70f15" + integrity sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-transform-arrow-functions@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz#10a59bebad52d637a027afa692e8d5ceff5e3dae" + integrity sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/plugin-transform-block-scoped-functions@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz#a9bf1836f2a39b4eb6cf09967739de29ea4bf4c4" + integrity sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-block-scoping@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz#f36e55076d06f41dfd78557ea039c1b581642e61" + integrity sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-classes@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz#0265155075c42918bf4d3a4053134176ad9b533b" + integrity sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.12.13" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-replace-supers" "^7.13.0" + "@babel/helper-split-export-declaration" "^7.12.13" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz#845c6e8b9bb55376b1fa0b92ef0bdc8ea06644ed" + integrity sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/plugin-transform-destructuring@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz#c5dce270014d4e1ebb1d806116694c12b7028963" + integrity sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/plugin-transform-flow-strip-types@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.13.0.tgz#58177a48c209971e8234e99906cb6bd1122addd3" + integrity sha512-EXAGFMJgSX8gxWD7PZtW/P6M+z74jpx3wm/+9pn+c2dOawPpBkUX7BrfyPvo6ZpXbgRIEuwgwDb/MGlKvu2pOg== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/plugin-syntax-flow" "^7.12.13" + +"@babel/plugin-transform-for-of@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz#c799f881a8091ac26b54867a845c3e97d2696062" + integrity sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/plugin-transform-function-name@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz#bb024452f9aaed861d374c8e7a24252ce3a50051" + integrity sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ== + dependencies: + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-literals@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz#2ca45bafe4a820197cf315794a4d26560fe4bdb9" + integrity sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-member-expression-literals@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz#5ffa66cd59b9e191314c9f1f803b938e8c081e40" + integrity sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-modules-commonjs@^7.0.0": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b" + integrity sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw== + dependencies: + "@babel/helper-module-transforms" "^7.13.0" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-simple-access" "^7.12.13" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-object-super@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz#b4416a2d63b8f7be314f3d349bd55a9c1b5171f7" + integrity sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-replace-supers" "^7.12.13" + +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz#8fa7603e3097f9c0b7ca1a4821bc2fb52e9e5007" + integrity sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/plugin-transform-property-literals@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz#4e6a9e37864d8f1b3bc0e2dce7bf8857db8b1a81" + integrity sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-react-display-name@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.13.tgz#c28effd771b276f4647411c9733dbb2d2da954bd" + integrity sha512-MprESJzI9O5VnJZrL7gg1MpdqmiFcUv41Jc7SahxYsNP2kDkFqClxxTZq+1Qv4AFCamm+GXMRDQINNn+qrxmiA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-react-jsx@^7.0.0": + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.17.tgz#dd2c1299f5e26de584939892de3cfc1807a38f24" + integrity sha512-mwaVNcXV+l6qJOuRhpdTEj8sT/Z0owAVWf9QujTZ0d2ye9X/K+MTOTSizcgKOj18PGnTc/7g1I4+cIUjsKhBcw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.12.13" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-jsx" "^7.12.13" + "@babel/types" "^7.12.17" + +"@babel/plugin-transform-runtime@^7.5.5": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.10.tgz#a1e40d22e2bf570c591c9c7e5ab42d6bf1e419e1" + integrity sha512-Y5k8ipgfvz5d/76tx7JYbKQTcgFSU6VgJ3kKQv4zGTKr+a9T/KBvfRvGtSFgKDQGt/DBykQixV0vNWKIdzWErA== + dependencies: + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + babel-plugin-polyfill-corejs2 "^0.1.4" + babel-plugin-polyfill-corejs3 "^0.1.3" + babel-plugin-polyfill-regenerator "^0.1.2" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz#db755732b70c539d504c6390d9ce90fe64aff7ad" + integrity sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-transform-spread@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz#84887710e273c1815ace7ae459f6f42a5d31d5fd" + integrity sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + +"@babel/plugin-transform-template-literals@^7.0.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz#a36049127977ad94438dee7443598d1cefdf409d" + integrity sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw== + dependencies: + "@babel/helper-plugin-utils" "^7.13.0" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" + integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.9.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/traverse@7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" + integrity sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.12.13" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" + integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.0" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.0" + "@babel/types" "^7.13.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" + integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.17", "@babel/types@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" + integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@ethersproject/abi@5.0.0-beta.153": + version "5.0.0-beta.153" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" + integrity sha512-aXweZ1Z7vMNzJdLpR1CZUAIgnwjrZeUSvN9syCwlBaEBUFJmFY+HHnfuTI5vIhVs/mRkfJVrbEyl51JZQqyjAg== + dependencies: + "@ethersproject/address" ">=5.0.0-beta.128" + "@ethersproject/bignumber" ">=5.0.0-beta.130" + "@ethersproject/bytes" ">=5.0.0-beta.129" + "@ethersproject/constants" ">=5.0.0-beta.128" + "@ethersproject/hash" ">=5.0.0-beta.128" + "@ethersproject/keccak256" ">=5.0.0-beta.127" + "@ethersproject/logger" ">=5.0.0-beta.129" + "@ethersproject/properties" ">=5.0.0-beta.131" + "@ethersproject/strings" ">=5.0.0-beta.130" + +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@^5.0.10", "@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@>=5.0.0-beta.128": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.11.tgz#12022e8c590c33939beb5ab18b401ecf585eac59" + integrity sha512-Et4GBdD8/tsBGjCEOKee9upN29qjL5kbRcmJifb4Penmiuh9GARXL2/xpXvEp5EW+EIW/rfCHFJrkYBgoQFQBw== + dependencies: + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/rlp" "^5.0.7" + +"@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.9", "@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/bignumber@>=5.0.0-beta.130": + version "5.0.15" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.15.tgz#b089b3f1e0381338d764ac1c10512f0c93b184ed" + integrity sha512-MTADqnyacvdRwtKh7o9ujwNDSM1SDJjYDMYAzjIgjoi9rh6TY4suMbhCa3i2vh3SUXiXSICyTI8ui+NPdrZ9Lw== + dependencies: + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" + bn.js "^4.4.0" + +"@ethersproject/bignumber@^5.0.13", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@>=5.0.0-beta.129": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.11.tgz#21118e75b1d00db068984c15530e316021101276" + integrity sha512-D51plLYY5qF05AsoVQwIZVLqlBkaTPVHVP/1WmmBIWyHB0cRW0C9kh0kx5Exo51rB63Hk8PfHxc7SmpoaQFEyg== + dependencies: + "@ethersproject/logger" "^5.0.8" + +"@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.9", "@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@>=5.0.0-beta.128": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.10.tgz#eb0c604fbc44c53ba9641eed31a1d0c9e1ebcadc" + integrity sha512-OSo8jxkHLDXieCy8bgOFR7lMfgPxEzKvSDdP+WAWHCDM8+orwch0B6wzkTmiQFgryAtIctrBt5glAdJikZ3hGw== + dependencies: + "@ethersproject/bignumber" "^5.0.13" + +"@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.0.8", "@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/hash@>=5.0.0-beta.128": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.12.tgz#1074599f7509e2ca2bb7a3d4f4e39ab3a796da42" + integrity sha512-kn4QN+fhNFbUgX3XZTZUaQixi0oyfIEY+hfW+KtkHu+rq7dV76oAIvaLEEynu1/4npOL38E4X4YI42gGZk+C0Q== + dependencies: + "@ethersproject/abstract-signer" "^5.0.10" + "@ethersproject/address" "^5.0.9" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/strings" "^5.0.8" + +"@ethersproject/hash@^5.0.4": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/keccak256@>=5.0.0-beta.127": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.9.tgz#ca0d86e4af56c13b1ef25e533bde3e96d28f647d" + integrity sha512-zhdUTj6RGtCJSgU+bDrWF6cGbvW453LoIC1DSNWrTlXzC7WuH4a+EiPrgc7/kNoRxerKuA/cxYlI8GwNtVtDlw== + dependencies: + "@ethersproject/bytes" "^5.0.9" + js-sha3 "0.5.7" + +"@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.0.7", "@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@>=5.0.0-beta.129": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.10.tgz#fd884688b3143253e0356ef92d5f22d109d2e026" + integrity sha512-0y2T2NqykDrbPM3Zw9RSbPkDOxwChAL8detXaom76CfYoGxsOnRP/zTX8OUAV+x9LdwzgbWvWmeXrc0M7SuDZw== + +"@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.0.8", "@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.0.tgz#df72a392f1a63a57f87210515695a31a245845ad" + integrity sha512-MG6oHSQHd4ebvJrleEQQ4HhVu8Ichr0RDYEfHzsVAVjHNM+w36x9wp9r+hf1JstMXtseXDtkiVoARAG6M959AA== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/properties@>=5.0.0-beta.131": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.9.tgz#d7aae634680760136ea522e25c3ef043ec15b5c2" + integrity sha512-ZCjzbHYTw+rF1Pn8FDCEmx3gQttwIHcm/6Xee8g/M3Ga3SfW4tccNMbs5zqnBH0E4RoOPaeNgyg1O68TaF0tlg== + dependencies: + "@ethersproject/logger" "^5.0.8" + +"@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.0.7", "@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@^5.0.7", "@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/signing-key@^5.0.8", "@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@>=5.0.0-beta.130": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.10.tgz#ddce1e9724f4ac4f3f67e0cac0b48748e964bfdb" + integrity sha512-KAeoS1tZ9/5ECXiIZA6S6hywbD0so2VmuW+Wfyo5EDXeyZ6Na1nxTPhTnW7voQmjbeYJffCrOc0qLFJeylyg7w== + dependencies: + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/logger" "^5.0.8" + +"@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.0.8", "@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@^5.0.0-beta.135": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.11.tgz#b31df5292f47937136a45885d6ee6112477c13df" + integrity sha512-ftsRvR9+gQp7L63F6+XmstvsZ4w8GtWvQB08e/zB+oB86Fnhq8+i/tkgpJplSHC8I/qgiCisva+M3u2GVhDFPA== + dependencies: + "@ethersproject/address" "^5.0.9" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/rlp" "^5.0.7" + "@ethersproject/signing-key" "^5.0.8" + +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/web@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.0.tgz#40850c05260edad8b54827923bbad23d96aac0bc" + integrity sha512-ApHcbbj+muRASVDSCl/tgxaH2LBkRMEYfLOLVa0COipx0+nlu0QKet7U2lEg0vdkh8XRSLf2nd1f1Uk9SrVSGA== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@graphprotocol/graph-cli@https://github.com/graphprotocol/graph-cli#main": + version "0.33.0" + resolved "https://github.com/graphprotocol/graph-cli#47e075a9701680580e0e8e09c5444963224dbf5c" + dependencies: + assemblyscript "0.19.10" + binary-install-raw "0.0.13" + chalk "3.0.0" + chokidar "3.5.1" + debug "4.3.1" + docker-compose "0.23.4" + dockerode "2.5.8" + fs-extra "9.0.0" + glob "7.1.6" + gluegun "https://github.com/edgeandnode/gluegun#v4.3.1-pin-colors-dep" + graphql "15.5.0" + immutable "3.8.2" + ipfs-http-client "34.0.0" + jayson "3.6.6" + js-yaml "3.13.1" + node-fetch "2.6.0" + pkginfo "0.4.1" + prettier "1.19.1" + request "2.88.2" + semver "7.3.5" + sync-request "6.1.0" + tmp-promise "3.0.2" + web3-eth-abi "1.7.0" + which "2.0.2" + yaml "1.9.2" + +"@graphprotocol/graph-ts@https://github.com/graphprotocol/graph-ts#main": + version "0.28.1" + resolved "https://github.com/graphprotocol/graph-ts#4e91d2c0b695c7689aba205516d3e80fb5588454" + dependencies: + assemblyscript "0.19.10" + +"@graphql-tools/batch-delegate@^6.2.4", "@graphql-tools/batch-delegate@^6.2.6": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-delegate/-/batch-delegate-6.2.6.tgz#fbea98dc825f87ef29ea5f3f371912c2a2aa2f2c" + integrity sha512-QUoE9pQtkdNPFdJHSnBhZtUfr3M7pIRoXoMR+TG7DK2Y62ISKbT/bKtZEUU1/2v5uqd5WVIvw9dF8gHDSJAsSA== + dependencies: + "@graphql-tools/delegate" "^6.2.4" + dataloader "2.0.0" + tslib "~2.0.1" + +"@graphql-tools/batch-execute@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-7.0.0.tgz#e79d11bd5b39f29172f6ec2eafa71103c6a6c85b" + integrity sha512-+ywPfK6N2Ddna6oOa5Qb1Mv7EA8LOwRNOAPP9dL37FEhksJM9pYqPSceUcqMqg7S9b0+Cgr78s408rgvurV3/Q== + dependencies: + "@graphql-tools/utils" "^7.0.0" + dataloader "2.0.0" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-tools/code-file-loader@^6.2.4": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-6.3.1.tgz#42dfd4db5b968acdb453382f172ec684fa0c34ed" + integrity sha512-ZJimcm2ig+avgsEOWWVvAaxZrXXhiiSZyYYOJi0hk9wh5BxZcLUNKkTp6EFnZE/jmGUwuos3pIjUD3Hwi3Bwhg== + dependencies: + "@graphql-tools/graphql-tag-pluck" "^6.5.1" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.1.0" + +"@graphql-tools/delegate@^6.2.4": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-6.2.4.tgz#db553b63eb9512d5eb5bbfdfcd8cb1e2b534699c" + integrity sha512-mXe6DfoWmq49kPcDrpKHgC2DSWcD5q0YCaHHoXYPAOlnLH8VMTY8BxcE8y/Do2eyg+GLcwAcrpffVszWMwqw0w== + dependencies: + "@ardatan/aggregate-error" "0.0.6" + "@graphql-tools/schema" "^6.2.4" + "@graphql-tools/utils" "^6.2.4" + dataloader "2.0.0" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-tools/delegate@^7.0.1", "@graphql-tools/delegate@^7.0.7": + version "7.0.10" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-7.0.10.tgz#f87ac85a2dbd03b5b3aabf347f4479fabe8ceac3" + integrity sha512-6Di9ia5ohoDvrHuhj2cak1nJGhIefJmUsd3WKZcJ2nu2yZAFawWMxGvQImqv3N7iyaWKiVhrrK8Roi/JrYhdKg== + dependencies: + "@ardatan/aggregate-error" "0.0.6" + "@graphql-tools/batch-execute" "^7.0.0" + "@graphql-tools/schema" "^7.0.0" + "@graphql-tools/utils" "^7.1.6" + dataloader "2.0.0" + is-promise "4.0.0" + tslib "~2.1.0" + +"@graphql-tools/git-loader@^6.2.4": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-6.2.6.tgz#c2226f4b8f51f1c05c9ab2649ba32d49c68cd077" + integrity sha512-ooQTt2CaG47vEYPP3CPD+nbA0F+FYQXfzrB1Y1ABN9K3d3O2RK3g8qwslzZaI8VJQthvKwt0A95ZeE4XxteYfw== + dependencies: + "@graphql-tools/graphql-tag-pluck" "^6.2.6" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.1.0" + +"@graphql-tools/github-loader@^6.2.4": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-6.2.5.tgz#460dff6f5bbaa26957a5ea3be4f452b89cc6a44b" + integrity sha512-DLuQmYeNNdPo8oWus8EePxWCfCAyUXPZ/p1PWqjrX/NGPyH2ZObdqtDAfRHztljt0F/qkBHbGHCEk2TKbRZTRw== + dependencies: + "@graphql-tools/graphql-tag-pluck" "^6.2.6" + "@graphql-tools/utils" "^7.0.0" + cross-fetch "3.0.6" + tslib "~2.0.1" + +"@graphql-tools/graphql-file-loader@^6.2.4": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-6.2.7.tgz#d3720f2c4f4bb90eb2a03a7869a780c61945e143" + integrity sha512-5k2SNz0W87tDcymhEMZMkd6/vs6QawDyjQXWtqkuLTBF3vxjxPD1I4dwHoxgWPIjjANhXybvulD7E+St/7s9TQ== + dependencies: + "@graphql-tools/import" "^6.2.6" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.1.0" + +"@graphql-tools/graphql-tag-pluck@^6.2.4", "@graphql-tools/graphql-tag-pluck@^6.2.6", "@graphql-tools/graphql-tag-pluck@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.5.1.tgz#5fb227dbb1e19f4b037792b50f646f16a2d4c686" + integrity sha512-7qkm82iFmcpb8M6/yRgzjShtW6Qu2OlCSZp8uatA3J0eMl87TxyJoUmL3M3UMMOSundAK8GmoyNVFUrueueV5Q== + dependencies: + "@babel/parser" "7.12.16" + "@babel/traverse" "7.12.13" + "@babel/types" "7.12.13" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.1.0" + +"@graphql-tools/import@^6.2.4", "@graphql-tools/import@^6.2.6": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.3.0.tgz#171472b425ea7cba4a612ad524b96bd206ae71b6" + integrity sha512-zmaVhJ3UPjzJSb005Pjn2iWvH+9AYRXI4IUiTi14uPupiXppJP3s7S25Si3+DbHpFwurDF2nWRxBLiFPWudCqw== + dependencies: + resolve-from "5.0.0" + tslib "~2.1.0" + +"@graphql-tools/json-file-loader@^6.2.4": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-6.2.6.tgz#830482cfd3721a0799cbf2fe5b09959d9332739a" + integrity sha512-CnfwBSY5926zyb6fkDBHnlTblHnHI4hoBALFYXnrg0Ev4yWU8B04DZl/pBRUc459VNgO2x8/mxGIZj2hPJG1EA== + dependencies: + "@graphql-tools/utils" "^7.0.0" + tslib "~2.0.1" + +"@graphql-tools/links@^6.2.4": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/links/-/links-6.2.5.tgz#b172cadc4b7cbe27bfc1dc787651f92517f583bc" + integrity sha512-XeGDioW7F+HK6HHD/zCeF0HRC9s12NfOXAKv1HC0J7D50F4qqMvhdS/OkjzLoBqsgh/Gm8icRc36B5s0rOA9ig== + dependencies: + "@graphql-tools/utils" "^7.0.0" + apollo-link "1.2.14" + apollo-upload-client "14.1.2" + cross-fetch "3.0.6" + form-data "3.0.0" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-tools/load-files@^6.2.4": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/load-files/-/load-files-6.3.0.tgz#3957e21e14eb078f68fb4ebe84702f1bdc03ca23" + integrity sha512-qDEMz3f5CQz8lIvIhzJVK6Fvd6TMMbhuqded4x5I6zWEetR4AUmwneHWnQkwyIRqDDGgy6VlBw7GToucUkvQag== + dependencies: + globby "11.0.2" + tslib "~2.1.0" + unixify "1.0.0" + +"@graphql-tools/load@^6.2.4": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-6.2.7.tgz#61f7909d37fb1c095e3e8d4f7a6d3b8bb011e26a" + integrity sha512-b1qWjki1y/QvGtoqW3x8bcwget7xmMfLGsvGFWOB6m38tDbzVT3GlJViAC0nGPDks9OCoJzAdi5IYEkBaqH5GQ== + dependencies: + "@graphql-tools/merge" "^6.2.9" + "@graphql-tools/utils" "^7.5.0" + globby "11.0.2" + import-from "3.0.0" + is-glob "4.0.1" + p-limit "3.1.0" + tslib "~2.1.0" + unixify "1.0.0" + valid-url "1.0.9" + +"@graphql-tools/merge@^6.2.4", "@graphql-tools/merge@^6.2.9": + version "6.2.10" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-6.2.10.tgz#cadb37b1bed786cba1b3c6f728c5476a164e153d" + integrity sha512-dM3n37PcslvhOAkCz7Cwk0BfoiSVKXGmCX+VMZkATbXk/0vlxUfNEpVfA5yF4IkP27F04SzFQSaNrbD0W2Rszw== + dependencies: + "@graphql-tools/schema" "^7.0.0" + "@graphql-tools/utils" "^7.5.0" + tslib "~2.1.0" + +"@graphql-tools/mock@^6.2.4": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/mock/-/mock-6.2.4.tgz#205323c51f89dd855d345d130c7713d0420909ea" + integrity sha512-O5Zvq/mcDZ7Ptky0IZ4EK9USmxV6FEVYq0Jxv2TI80kvxbCjt0tbEpZ+r1vIt1gZOXlAvadSHYyzWnUPh+1vkQ== + dependencies: + "@graphql-tools/schema" "^6.2.4" + "@graphql-tools/utils" "^6.2.4" + tslib "~2.0.1" + +"@graphql-tools/module-loader@^6.2.4": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/module-loader/-/module-loader-6.2.7.tgz#66ab9468775fac8079ca46ea9896ceea76e4ef69" + integrity sha512-ItAAbHvwfznY9h1H9FwHYDstTcm22Dr5R9GZtrWlpwqj0jaJGcBxsMB9jnK9kFqkbtFYEe4E/NsSnxsS4/vViQ== + dependencies: + "@graphql-tools/utils" "^7.5.0" + tslib "~2.1.0" + +"@graphql-tools/relay-operation-optimizer@^6.2.4": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz#f8c7f6c8aa4a9cf50ab151fbc5db4f4282a79532" + integrity sha512-Or3UgRvkY9Fq1AAx7q38oPqFmTepLz7kp6wDHKyR0ceG7AvHv5En22R12mAeISInbhff4Rpwgf6cE8zHRu6bCw== + dependencies: + "@graphql-tools/utils" "^7.1.0" + relay-compiler "10.1.0" + tslib "~2.0.1" + +"@graphql-tools/resolvers-composition@^6.2.4": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/resolvers-composition/-/resolvers-composition-6.2.6.tgz#b369cdf2772a41a7544bf3f16a794501da34c394" + integrity sha512-QO0PC5RG0SolOksupOuB4B0tuzEsQFwQrwD9xLHCrJmjaLi66lOKMFzN40IBY5rqg0k/zqPyjII8rtzcNobvIg== + dependencies: + "@graphql-tools/utils" "^7.0.0" + lodash "4.17.21" + tslib "~2.1.0" + +"@graphql-tools/schema@^6.2.4": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-6.2.4.tgz#cc4e9f5cab0f4ec48500e666719d99fc5042481d" + integrity sha512-rh+14lSY1q8IPbEv2J9x8UBFJ5NrDX9W5asXEUlPp+7vraLp/Tiox4GXdgyA92JhwpYco3nTf5Bo2JDMt1KnAQ== + dependencies: + "@graphql-tools/utils" "^6.2.4" + tslib "~2.0.1" + +"@graphql-tools/schema@^7.0.0", "@graphql-tools/schema@^7.1.2": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-7.1.3.tgz#d816400da51fbac1f0086e35540ab63b5e30e858" + integrity sha512-ZY76hmcJlF1iyg3Im0sQ3ASRkiShjgv102vLTVcH22lEGJeCaCyyS/GF1eUHom418S60bS8Th6+autRUxfBiBg== + dependencies: + "@graphql-tools/utils" "^7.1.2" + tslib "~2.1.0" + +"@graphql-tools/stitch@^6.2.4": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/stitch/-/stitch-6.2.4.tgz#acfa6a577a33c0f02e4940ffff04753b23b87fd6" + integrity sha512-0C7PNkS7v7iAc001m7c1LPm5FUB0/DYw+s3OyCii6YYYHY8NwdI0roeOyeDGFJkFubWBQfjc3hoSyueKtU73mw== + dependencies: + "@graphql-tools/batch-delegate" "^6.2.4" + "@graphql-tools/delegate" "^6.2.4" + "@graphql-tools/merge" "^6.2.4" + "@graphql-tools/schema" "^6.2.4" + "@graphql-tools/utils" "^6.2.4" + "@graphql-tools/wrap" "^6.2.4" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-tools/url-loader@^6.2.4": + version "6.8.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-6.8.1.tgz#cbfbe20f1a1bdeb9a4704e37b8286026d228920b" + integrity sha512-iE/y9IAu0cZYL7o9IIDdGm5WjxacN25nGgVqjZINYlisW/wyuBxng7DMJBAp6yM6gkxkCpMno1ljA/52MXzVPQ== + dependencies: + "@graphql-tools/delegate" "^7.0.1" + "@graphql-tools/utils" "^7.1.5" + "@graphql-tools/wrap" "^7.0.4" + "@types/websocket" "1.0.1" + cross-fetch "3.0.6" + eventsource "1.0.7" + extract-files "9.0.0" + form-data "4.0.0" + graphql-upload "^11.0.0" + graphql-ws "4.1.5" + is-promise "4.0.0" + isomorphic-ws "4.0.1" + sse-z "0.3.0" + sync-fetch "0.3.0" + tslib "~2.1.0" + valid-url "1.0.9" + ws "7.4.3" + +"@graphql-tools/utils@^6.2.4": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.2.4.tgz#38a2314d2e5e229ad4f78cca44e1199e18d55856" + integrity sha512-ybgZ9EIJE3JMOtTrTd2VcIpTXtDrn2q6eiYkeYMKRVh3K41+LZa6YnR2zKERTXqTWqhobROwLt4BZbw2O3Aeeg== + dependencies: + "@ardatan/aggregate-error" "0.0.6" + camel-case "4.1.1" + tslib "~2.0.1" + +"@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.0", "@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.1.5", "@graphql-tools/utils@^7.1.6", "@graphql-tools/utils@^7.2.1", "@graphql-tools/utils@^7.5.0": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.5.1.tgz#1c77ca69ffeb428e8ec51e661413bc6a5594268b" + integrity sha512-FYhSdJrU5cZ8BRuzCVV+YixLx3mXYVzowpKGPfI7re9/WvQPjlyIcjG+hd0C4u/L9Dxx46nBkiqZxZZknE6/lA== + dependencies: + "@ardatan/aggregate-error" "0.0.6" + camel-case "4.1.2" + tslib "~2.1.0" + +"@graphql-tools/wrap@^6.2.4": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-6.2.4.tgz#2709817da6e469753735a9fe038c9e99736b2c57" + integrity sha512-cyQgpybolF9DjL2QNOvTS1WDCT/epgYoiA8/8b3nwv5xmMBQ6/6nYnZwityCZ7njb7MMyk7HBEDNNlP9qNJDcA== + dependencies: + "@graphql-tools/delegate" "^6.2.4" + "@graphql-tools/schema" "^6.2.4" + "@graphql-tools/utils" "^6.2.4" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-tools/wrap@^7.0.4": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-7.0.5.tgz#8659a119abef11754f712b0c202e41a484951e0b" + integrity sha512-KCWBXsDfvG46GNUawRltJL4j9BMGoOG7oo3WEyCQP+SByWXiTe5cBF45SLDVQgdjljGNZhZ4Lq/7avIkF7/zDQ== + dependencies: + "@graphql-tools/delegate" "^7.0.7" + "@graphql-tools/schema" "^7.1.2" + "@graphql-tools/utils" "^7.2.1" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-typed-document-node/core@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.0.tgz#0eee6373e11418bfe0b5638f654df7a4ca6a3950" + integrity sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg== + +"@gulp-sourcemaps/map-sources@1.X": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda" + integrity sha1-iQrnxdjId/bThIYCFazp1+yUW9o= + dependencies: + normalize-path "^2.0.1" + through2 "^2.0.3" + +"@josephg/resolvable@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@josephg/resolvable/-/resolvable-1.0.1.tgz#69bc4db754d79e1a2f17a650d3466e038d94a5eb" + integrity sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg== + +"@nodelib/fs.scandir@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" + integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== + dependencies: + "@nodelib/fs.stat" "2.0.4" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" + integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" + integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== + dependencies: + "@nodelib/fs.scandir" "2.1.4" + fastq "^1.6.0" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + +"@redux-saga/core@^1.0.0": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.1.3.tgz#3085097b57a4ea8db5528d58673f20ce0950f6a4" + integrity sha512-8tInBftak8TPzE6X13ABmEtRJGjtK17w7VUs7qV17S8hCO5S3+aUTWZ/DBsBJPdE8Z5jOPwYALyvofgq1Ws+kg== + dependencies: + "@babel/runtime" "^7.6.3" + "@redux-saga/deferred" "^1.1.2" + "@redux-saga/delay-p" "^1.1.2" + "@redux-saga/is" "^1.1.2" + "@redux-saga/symbols" "^1.1.2" + "@redux-saga/types" "^1.1.0" + redux "^4.0.4" + typescript-tuple "^2.2.1" + +"@redux-saga/deferred@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@redux-saga/deferred/-/deferred-1.1.2.tgz#59937a0eba71fff289f1310233bc518117a71888" + integrity sha512-908rDLHFN2UUzt2jb4uOzj6afpjgJe3MjICaUNO3bvkV/kN/cNeI9PMr8BsFXB/MR8WTAZQq/PlTq8Kww3TBSQ== + +"@redux-saga/delay-p@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@redux-saga/delay-p/-/delay-p-1.1.2.tgz#8f515f4b009b05b02a37a7c3d0ca9ddc157bb355" + integrity sha512-ojc+1IoC6OP65Ts5+ZHbEYdrohmIw1j9P7HS9MOJezqMYtCDgpkoqB5enAAZrNtnbSL6gVCWPHaoaTY5KeO0/g== + dependencies: + "@redux-saga/symbols" "^1.1.2" + +"@redux-saga/is@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@redux-saga/is/-/is-1.1.2.tgz#ae6c8421f58fcba80faf7cadb7d65b303b97e58e" + integrity sha512-OLbunKVsCVNTKEf2cH4TYyNbbPgvmZ52iaxBD4I1fTif4+MTXMa4/Z07L83zW/hTCXwpSZvXogqMqLfex2Tg6w== + dependencies: + "@redux-saga/symbols" "^1.1.2" + "@redux-saga/types" "^1.1.0" + +"@redux-saga/symbols@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@redux-saga/symbols/-/symbols-1.1.2.tgz#216a672a487fc256872b8034835afc22a2d0595d" + integrity sha512-EfdGnF423glv3uMwLsGAtE6bg+R9MdqlHEzExnfagXPrIiuxwr3bdiAwz3gi+PsrQ3yBlaBpfGLtDG8rf3LgQQ== + +"@redux-saga/types@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.1.0.tgz#0e81ce56b4883b4b2a3001ebe1ab298b84237204" + integrity sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg== + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/abi-utils@^0.1.4", "@truffle/abi-utils@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@truffle/abi-utils/-/abi-utils-0.1.5.tgz#95b39ee0cb6baf777fdbaa2ac6d901ab8b0f8c58" + integrity sha512-PvCN/qebM0boK2YycX3sMe6CwoLtB7cpYj2ugHPtcQ+Zpg1hQRGS+GRLeBuQg3RR5X8IxzLb4YPZh5dnJxMZYA== + dependencies: + change-case "3.0.2" + faker "^5.3.1" + fast-check "^2.12.1" + source-map-support "^0.5.19" + +"@truffle/blockchain-utils@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.26.tgz#f4ea794e0a18c74d73ea10e29a506c9ed0a503ee" + integrity sha512-M91NJkfapK1RqdzVwKSSenPEE2cHzAAFwC3aPhA8Y3DznRfzOcck4mDH6eY71sytVCrGaXGm/Wirn3drGSH+qQ== + dependencies: + source-map-support "^0.5.19" + +"@truffle/code-utils@^1.2.23", "@truffle/code-utils@^1.2.24": + version "1.2.24" + resolved "https://registry.yarnpkg.com/@truffle/code-utils/-/code-utils-1.2.24.tgz#8da82510e416128c45fc154e92410982ab98b426" + integrity sha512-IqpbTh4uNQueadv96GBWBaGTYTyOsLKE9Dui1wpiijON6xq2iIcTArej1vMh+nkAd5/AsP+enbBY8mksm6rFBg== + dependencies: + cbor "^5.1.0" + source-map-support "^0.5.19" + +"@truffle/codec@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.10.1.tgz#70df52ddf1c64781a23daaccda24e10bfb9dec9d" + integrity sha512-c1lC9Wcp+Z1DLvEYH3dkEtMKnUJx72CirO3kmi0OgFSA5QqTDCtfrVOhAugcb/iMLgqUK05/pexp2whb4oASKA== + dependencies: + big.js "^5.2.2" + bn.js "^5.1.3" + cbor "^5.1.0" + debug "^4.3.1" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.partition "^4.6.0" + lodash.sum "^4.0.2" + semver "^7.3.4" + source-map-support "^0.5.19" + utf8 "^3.0.0" + web3-utils "1.2.9" + +"@truffle/config@^1.2.35": + version "1.2.35" + resolved "https://registry.yarnpkg.com/@truffle/config/-/config-1.2.35.tgz#98a9ae3a964e73c33dcea4dcb172f878fdbb9bdd" + integrity sha512-ULTS9t3ldqEV1VBVNWlS9tdWJ0r637ANspzBoQd6S/Ab7CfueQhcIfp29oz6Ahcgjkl4NX+Gu/dG6/Jiys81vg== + dependencies: + "@truffle/error" "^0.0.12" + "@truffle/events" "^0.0.9" + "@truffle/provider" "^0.2.26" + configstore "^4.0.0" + find-up "^2.1.0" + lodash.assignin "^4.2.0" + lodash.merge "^4.6.2" + module "^1.2.5" + original-require "^1.0.1" + source-map-support "^0.5.19" + +"@truffle/contract-schema@^3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.3.4.tgz#95f0265cac7de7bcaa0542f5fe671a7896011bfe" + integrity sha512-HzscBl/GhZBvPNQeD9l6ewSHSkvNmE+bA0iTVa0Y2mNf5GD5Y3fK2NPyfbOdtckOvLqebvYGEDEPRiXc3BZ05g== + dependencies: + ajv "^6.10.0" + crypto-js "^3.1.9-1" + debug "^4.3.1" + +"@truffle/contract@^4.3": + version "4.3.9" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.9.tgz#caf515df359e72f207edc6f1d4e7b8bca88566a7" + integrity sha512-yd6nejsKEReJrPjOdRHkypfsMr337yc43qxu5b4TF2JAf2Kz7ZAWasHhY3j3xRwra3AqNOm4p3njkq8T+mKytg== + dependencies: + "@truffle/blockchain-utils" "^0.0.26" + "@truffle/contract-schema" "^3.3.4" + "@truffle/debug-utils" "^5.0.11" + "@truffle/error" "^0.0.12" + "@truffle/interface-adapter" "^0.4.19" + bignumber.js "^7.2.1" + ethereum-ens "^0.8.0" + ethers "^4.0.32" + source-map-support "^0.5.19" + web3 "1.2.9" + web3-core-helpers "1.2.9" + web3-core-promievent "1.2.9" + web3-eth-abi "1.2.9" + web3-utils "1.2.9" + +"@truffle/db@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@truffle/db/-/db-0.5.3.tgz#1223ee5c9b9f112abf5883f647d46ae1e45d5dbd" + integrity sha512-cNQJgcqC77xAIvFrS9R1XHmppOnlZmVZvcEqHOv0PGzcf0XA+hUkUgiOHFYn5bwTvGxLMrBlBmAnBprKlJYsRg== + dependencies: + "@truffle/abi-utils" "^0.1.4" + "@truffle/code-utils" "^1.2.23" + "@truffle/config" "^1.2.35" + apollo-server "^2.18.2" + debug "^4.3.1" + fs-extra "^9.1.0" + graphql "^15.3.0" + graphql-tag "^2.11.0" + graphql-tools "^6.2.4" + json-stable-stringify "^1.0.1" + jsondown "^1.0.0" + pascal-case "^2.0.1" + pluralize "^8.0.0" + pouchdb "7.1.1" + pouchdb-adapter-memory "^7.1.1" + pouchdb-adapter-node-websql "^7.0.0" + pouchdb-debug "^7.1.1" + pouchdb-find "^7.0.0" + source-map-support "^0.5.19" + web3-utils "1.2.9" + +"@truffle/debug-utils@^5.0.11": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.11.tgz#297ff83943212bf593a641180e3b28b230acadaa" + integrity sha512-KurW9r1DcK9c7/I0H21YWGBKu77gWm5HfBW6T+MjuRh5FGpxZ7GPka8oQkJCAZQuZKaQc9r9BoCQYQx1NX8pIg== + dependencies: + "@truffle/codec" "^0.10.1" + "@trufflesuite/chromafi" "^2.2.2" + bn.js "^5.1.3" + chalk "^2.4.2" + debug "^4.3.1" + highlight.js "^10.4.0" + highlightjs-solidity "^1.0.21" + +"@truffle/debugger@^8.0.17": + version "8.0.17" + resolved "https://registry.yarnpkg.com/@truffle/debugger/-/debugger-8.0.17.tgz#a13cd3c967bf045e71a00bd711fb371effa06752" + integrity sha512-CpxsW3edO0gPygLUmIkhFC4hgONltYuhJIM8jhdYL4KpDe8hRlFjWeiveH++iJX/1ka1A2Wbyk9G/TtCdiLY4Q== + dependencies: + "@truffle/abi-utils" "^0.1.5" + "@truffle/codec" "^0.10.1" + "@truffle/source-map-utils" "^1.3.35" + bn.js "^5.1.3" + debug "^4.3.1" + json-pointer "^0.6.0" + json-stable-stringify "^1.0.1" + lodash.flatten "^4.4.0" + lodash.merge "^4.6.2" + lodash.sum "^4.0.2" + lodash.zipwith "^4.2.0" + redux "^3.7.2" + redux-cli-logger "^2.0.1" + redux-saga "1.0.0" + remote-redux-devtools "^0.5.12" + reselect-tree "^1.3.4" + semver "^7.3.4" + source-map-support "^0.5.19" + web3 "1.2.9" + web3-eth-abi "1.2.9" + +"@truffle/error@^0.0.12": + version "0.0.12" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.12.tgz#83e02e6ffe1d154fe274141d90038a91fd1e186d" + integrity sha512-kZqqnPR9YDJG7KCDOcN1qH16Qs0oz1PzF0Y93AWdhXuL9S9HYo/RUUeqGKbPpRBEZldQUS8aa4EzfK08u5pu6g== + +"@truffle/events@^0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@truffle/events/-/events-0.0.9.tgz#460fc72a04269526cbd8ef54069d474c22b42b23" + integrity sha512-o0rS8zkjCzg2vDJymSZyyq1eKdkRbxIFnsnYQl6Bc2StK89C/ZISenxrUe2fbdeq3L9Zq+ds1mSKH/MFK0Ejkg== + dependencies: + emittery "^0.4.1" + ora "^3.4.0" + +"@truffle/hdwallet-provider@^1.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.2.2.tgz#7b42f7cb7fc1f80751c573c72ba488e59690f8ea" + integrity sha512-gpE5M9c+G7uMR9Nn2xslY0BRdl8hvlrHxBJ451g/V3WnOI5rDQMXezz6VZMn3zvWDiQTPRknx1uUDfWvMuQwqg== + dependencies: + "@trufflesuite/web3-provider-engine" "15.0.13-1" + any-promise "^1.3.0" + bindings "^1.5.0" + ethereum-cryptography "^0.1.3" + ethereum-protocol "^1.0.1" + ethereumjs-tx "^1.0.0" + ethereumjs-util "^6.1.0" + ethereumjs-wallet "^1.0.1" + source-map-support "^0.5.19" + +"@truffle/interface-adapter@^0.4.19": + version "0.4.19" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.4.19.tgz#19248ac88099f8df34f58a3d43a95ba3470dc89a" + integrity sha512-+Zz6Fr8+I2wYSS8RM3WBOMzf22QffMQTnlsYsRgRHzv3gYoRA9ZDLb84lFRfmWyw+IdXTo90tjRHEb5krC6uxg== + dependencies: + bn.js "^5.1.3" + ethers "^4.0.32" + source-map-support "^0.5.19" + web3 "1.2.9" + +"@truffle/provider@^0.2.26": + version "0.2.26" + resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.26.tgz#88e31b79973c2427c4a17d9a59411e6fbc810190" + integrity sha512-YKPmhB9S9AQkT2ePGtadwjDduxU23DXXy+5zyM5fevw5GCbXSnf+jG6rICXjPkVFjuKBlXuq5JbuERZn43522Q== + dependencies: + "@truffle/error" "^0.0.12" + "@truffle/interface-adapter" "^0.4.19" + web3 "1.2.9" + +"@truffle/source-map-utils@^1.3.35": + version "1.3.35" + resolved "https://registry.yarnpkg.com/@truffle/source-map-utils/-/source-map-utils-1.3.35.tgz#aa40422a05e2727254665ee2c23659d01230eb8f" + integrity sha512-j3PHac4g/yQwxSB899lkal/YMuIXLNNlDGfCog2QrWqdtK7HJhx6X2tftwqrZzO4JTKc1Cs8KOCPOndx9W2xeQ== + dependencies: + "@truffle/code-utils" "^1.2.24" + "@truffle/codec" "^0.10.1" + debug "^4.3.1" + json-pointer "^0.6.0" + node-interval-tree "^1.3.3" + web3-utils "1.2.9" + +"@trufflesuite/chromafi@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz#d3fc507aa8504faffc50fb892cedcfe98ff57f77" + integrity sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA== + dependencies: + ansi-mark "^1.0.0" + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + camelcase "^4.1.0" + chalk "^2.3.2" + cheerio "^1.0.0-rc.2" + detect-indent "^5.0.0" + he "^1.1.1" + highlight.js "^10.4.1" + lodash.merge "^4.6.2" + min-indent "^1.0.0" + strip-ansi "^4.0.0" + strip-indent "^2.0.0" + super-split "^1.1.0" + +"@trufflesuite/eth-json-rpc-filters@^4.1.2-1": + version "4.1.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.2-1.tgz#61ab78c52e98a883e5cf086925b34a30297b1824" + integrity sha512-/MChvC5dw2ck9NU1cZmdovCz2VKbOeIyR4tcxDvA5sT+NaL0rA2/R5U0yI7zsbo1zD+pgqav77rQHTzpUdDNJQ== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-0" + await-semaphore "^0.1.3" + eth-query "^2.1.2" + json-rpc-engine "^5.1.3" + lodash.flatmap "^4.5.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-json-rpc-infura@^4.0.3-0": + version "4.0.3-0" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-infura/-/eth-json-rpc-infura-4.0.3-0.tgz#6d22122937cf60ec9d21a02351c101fdc608c4fe" + integrity sha512-xaUanOmo0YLqRsL0SfXpFienhdw5bpQ1WEXxMTRi57az4lwpZBv4tFUDvcerdwJrxX9wQqNmgUgd1BrR01dumw== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + cross-fetch "^2.1.1" + eth-json-rpc-errors "^1.0.1" + json-rpc-engine "^5.1.3" + +"@trufflesuite/eth-json-rpc-middleware@^4.4.2-0", "@trufflesuite/eth-json-rpc-middleware@^4.4.2-1": + version "4.4.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.2-1.tgz#8c3638ed8a7ed89a1e5e71407de068a65bef0df2" + integrity sha512-iEy9H8ja7/8aYES5HfrepGBKU9n/Y4OabBJEklVd/zIBlhCCBAWBqkIZgXt11nBXO/rYAeKwYuE3puH3ByYnLA== + dependencies: + "@trufflesuite/eth-sig-util" "^1.4.2" + btoa "^1.2.1" + clone "^2.1.1" + eth-json-rpc-errors "^1.0.1" + eth-query "^2.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.7" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.6.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^5.1.3" + json-stable-stringify "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-sig-util@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-sig-util/-/eth-sig-util-1.4.2.tgz#b529e2f38ac08e652116f48981132a26242a4f08" + integrity sha512-+GyfN6b0LNW77hbQlH3ufZ/1eCON7mMrGym6tdYf7xiNw9Vv3jBO72bmmos1EId2NgBvPMhmYYm6DSLQFTmzrA== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^5.1.1" + +"@trufflesuite/web3-provider-engine@15.0.13-1": + version "15.0.13-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/web3-provider-engine/-/web3-provider-engine-15.0.13-1.tgz#f6a7f7131a2fdc4ab53976318ed13ce83e8e4bcb" + integrity sha512-6u3x/iIN5fyj8pib5QTUDmIOUiwAGhaqdSTXdqCu6v9zo2BEwdCqgEJd1uXDh3DBmPRDfiZ/ge8oUPy7LerpHg== + dependencies: + "@trufflesuite/eth-json-rpc-filters" "^4.1.2-1" + "@trufflesuite/eth-json-rpc-infura" "^4.0.3-0" + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + "@trufflesuite/eth-sig-util" "^1.4.2" + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^4.4.2" + eth-json-rpc-errors "^2.0.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +"@types/accepts@*", "@types/accepts@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" + integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== + dependencies: + "@types/node" "*" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/body-parser@*": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" + integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/body-parser@1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/concat-stream@^1.6.0": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74" + integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA== + dependencies: + "@types/node" "*" + +"@types/connect@*", "@types/connect@^3.4.33": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.4.tgz#de48cf01c79c9f1560bcfd8ae43217ab028657f8" + integrity sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ== + +"@types/cookies@*": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.7.tgz#7a92453d1d16389c05a5301eef566f34946cfd81" + integrity sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + +"@types/cors@2.8.10": + version "2.8.10" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" + integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== + +"@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.21": + version "4.17.24" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" + integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express-serve-static-core@^4.17.9": + version "4.17.30" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz#0f2f99617fa8f9696170c46152ccf7500b34ac04" + integrity sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.12": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/form-data@0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" + integrity sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw== + dependencies: + "@types/node" "*" + +"@types/fs-capacitor@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/fs-capacitor/-/fs-capacitor-2.0.0.tgz#17113e25817f584f58100fb7a08eed288b81956e" + integrity sha512-FKVPOCFbhCvZxpVAMhdBdTfVfXUpsh15wFHgqOKxh9N9vzWZVuWCSijZ5T4U34XYNnuj2oduh6xcs1i+LPI+BQ== + dependencies: + "@types/node" "*" + +"@types/http-assert@*": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.3.tgz#ef8e3d1a8d46c387f04ab0f2e8ab8cb0c5078661" + integrity sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA== + +"@types/http-errors@*": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.1.tgz#e81ad28a60bee0328c6d2384e029aec626f1ae67" + integrity sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q== + +"@types/keygrip@*": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" + integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== + +"@types/koa-compose@*": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" + integrity sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ== + dependencies: + "@types/koa" "*" + +"@types/koa@*": + version "2.13.4" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" + integrity sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/lodash@^4.14.159": + version "4.14.184" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.184.tgz#23f96cd2a21a28e106dc24d825d4aa966de7a9fe" + integrity sha512-RoZphVtHbxPZizt4IcILciSWiC6dcn+eZ8oX9IWEYfDMcocdd42f7NPI6fQj+6zI8y4E0L7gu2pcZKLGTRaV9Q== + +"@types/long@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/node@*": + version "18.7.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.11.tgz#486e72cfccde88da24e1f23ff1b7d8bfb64e6250" + integrity sha512-KZhFpSLlmK/sdocfSAjqPETTMd0ug6HIMIAwkwUpU79olnZdQtMxpQP+G1wDzCH7na+FltSIhbaZuKdwZ8RDrw== + +"@types/node@^10.0.3", "@types/node@^10.1.0": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + +"@types/node@^10.12.18": + version "10.17.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.55.tgz#a147f282edec679b894d4694edb5abeb595fecbd" + integrity sha512-koZJ89uLZufDvToeWO5BrC4CR4OUfHnUz2qoPs/daQH6qq3IN62QFxCTZ+bKaCE0xaoCAJYE4AXre8AbghCrhg== + +"@types/node@^12.12.54": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/node@^12.12.6", "@types/node@^12.6.1": + version "12.20.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.4.tgz#73687043dd00fcb6962c60fbf499553a24d6bdf2" + integrity sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ== + +"@types/node@^8.0.0": + version "8.10.66" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" + integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/qs@*", "@types/qs@^6.2.31": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/secp256k1@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" + integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + dependencies: + "@types/node" "*" + +"@types/serve-static@*": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/ungap__global-this@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@types/ungap__global-this/-/ungap__global-this-0.3.1.tgz#18ce9f657da556037a29d50604335614ce703f4c" + integrity sha512-+/DsiV4CxXl6ZWefwHZDXSe1Slitz21tom38qPCaG0DYCS1NnDPIQDTKcmQ/tvK/edJUKkmuIDBJbmKDiB0r/g== + +"@types/websocket@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.1.tgz#039272c196c2c0e4868a0d8a1a27bbb86e9e9138" + integrity sha512-f5WLMpezwVxCLm1xQe/kdPpQIOmL0TXYx2O15VYfYzc7hTIdxiOoOvez+McSIw3b7z/1zGovew9YSL7+h4h7/Q== + dependencies: + "@types/node" "*" + +"@types/ws@^7.0.0", "@types/ws@^7.4.4": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + +"@types/zen-observable@^0.8.0": + version "0.8.2" + resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" + integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== + +"@ungap/global-this@^0.4.2": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@ungap/global-this/-/global-this-0.4.4.tgz#8a1b2cfcd3e26e079a847daba879308c924dd695" + integrity sha512-mHkm6FvepJECMNthFuIgpAEFmPOk71UyXuIxYfjytvFTnSDBIz7jmViO+LfHI/AjrazWije0PnSP3+/NlwzqtA== + +"@wry/context@^0.5.2": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.5.4.tgz#b6c28038872e0a0e1ff14eb40b5bf4cab2ab4e06" + integrity sha512-/pktJKHUXDr4D6TJqWgudOPJW2Z+Nb+bqk40jufA3uTkLbnCRKdJPiYDIa/c7mfcPH8Hr6O8zjCERpg5Sq04Zg== + dependencies: + tslib "^1.14.1" + +"@wry/equality@^0.1.2": + version "0.1.11" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790" + integrity sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA== + dependencies: + tslib "^1.9.3" + +"@wry/equality@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.3.4.tgz#37f101552b18a046d5c0c06da7b2021b15f72c03" + integrity sha512-1gQQhCPenzxw/1HzLlvSIs/59eBHJf9ZDIussjjZhqNSqQuPKQIzN6SWt4kemvlBPDi7RqMuUa03pId7MAE93g== + dependencies: + tslib "^1.14.1" + +"@wry/trie@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.2.2.tgz#99f20f0fcbbcda17006069b155c826cbabfc402f" + integrity sha512-OxqBB39x6MfHaa2HpMiRMfhuUnQTddD32Ko020eBeJXq87ivX6xnSSnzKHVbA21p7iqBASz8n/07b6W5wW1BVQ== + dependencies: + tslib "^1.14.1" + +JSONStream@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" + integrity sha512-mn0KSip7N4e0UDPZHnqDsHECo5uGQrixQKnAskOM1BIB8hd7QKbd6il8IPRPudPHOeHiECoCFqhyMaRO9+nWyA== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abab@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" + integrity sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4= + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abort-controller@3.0.0, abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~6.0.0, abstract-leveldown@~6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz#b4b6159343c74b0c5197b2817854782d8f748c4a" + integrity sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q== + dependencies: + level-concat-iterator "~2.0.0" + xtend "~4.0.0" + +abstract-leveldown@~6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +accepts@^1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-globals@^1.0.4: + version "1.0.9" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" + integrity sha1-VbtemGkVB7dFedBRNBMhfDgMVM8= + dependencies: + acorn "^2.1.0" + +acorn@4.X: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= + +acorn@^2.1.0, acorn@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" + integrity sha1-q259nYhqrKiwhbwzEreaGYQz8Oc= + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +ajv@^6.10.0, ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-colors@^3.2.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-mark@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ansi-mark/-/ansi-mark-1.0.4.tgz#1cd4ba8d57f15f109d6aaf6ec9ca9786c8a4ee6c" + integrity sha1-HNS6jVfxXxCdaq9uycqXhsik7mw= + dependencies: + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + chalk "^2.3.2" + strip-ansi "^4.0.0" + super-split "^1.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +anymatch@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +apisauce@^1.0.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/apisauce/-/apisauce-1.1.5.tgz#31d41a5cf805e401266cec67faf1a50f4aeae234" + integrity sha512-gKC8qb/bDJsPsnEXLZnXJ7gVx7dh87CEVNeIwv1dvaffnXoh5GHwac5pWR1P2broLiVj/fqFMQvLDDt/RhjiqA== + dependencies: + axios "^0.21.2" + ramda "^0.25.0" + +apisauce@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/apisauce/-/apisauce-2.0.1.tgz#cf5af56ea6ff5145e6eeb8d4ba471c7e0662b8c4" + integrity sha512-mJBw3pKmtfVoP6oifnf7/iRJQtNkVb6GkYsVOXN2pidootj1mhGBtzYHOX9FVBzAz5QV2GMu8IJtiNIgZ44kHQ== + dependencies: + axios "^0.21.1" + ramda "^0.25.0" + +apollo-cache-control@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.14.0.tgz#95f20c3e03e7994e0d1bd48c59aeaeb575ed0ce7" + integrity sha512-qN4BCq90egQrgNnTRMUHikLZZAprf3gbm8rC5Vwmc6ZdLolQ7bFsa769Hqi6Tq/lS31KLsXBLTOsRbfPHph12w== + dependencies: + apollo-server-env "^3.1.0" + apollo-server-plugin-base "^0.13.0" + +apollo-datasource@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.9.0.tgz#b0b2913257a6103a5f4c03cb56d78a30e9d850db" + integrity sha512-y8H99NExU1Sk4TvcaUxTdzfq2SZo6uSj5dyh75XSQvbpH6gdAXIW9MaBcvlNC7n0cVPsidHmOcHOWxJ/pTXGjA== + dependencies: + apollo-server-caching "^0.7.0" + apollo-server-env "^3.1.0" + +apollo-fetch@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/apollo-fetch/-/apollo-fetch-0.7.0.tgz#63c255a0ccb1b4c473524d8f9b536d72438bd3e7" + integrity sha512-0oHsDW3Zxx+Of1wuqcOXruNj4Kv55WN69tkIjwkCQDEIrgCpgA2scjChFsgflSVMy/1mkTKCY1Mc0TYJhNRzmw== + dependencies: + cross-fetch "^1.0.0" + +apollo-graphql@^0.9.0: + version "0.9.5" + resolved "https://registry.yarnpkg.com/apollo-graphql/-/apollo-graphql-0.9.5.tgz#9113483ca7f7fa49ee9e9a299c45d30b1cf3bf61" + integrity sha512-RGt5k2JeBqrmnwRM0VOgWFiGKlGJMfmiif/4JvdaEqhMJ+xqe/9cfDYzXfn33ke2eWixsAbjEbRfy8XbaN9nTw== + dependencies: + core-js-pure "^3.10.2" + lodash.sortby "^4.7.0" + sha.js "^2.4.11" + +apollo-link@1.2.14, apollo-link@^1.2.14: + version "1.2.14" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9" + integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg== + dependencies: + apollo-utilities "^1.3.0" + ts-invariant "^0.4.0" + tslib "^1.9.3" + zen-observable-ts "^0.8.21" + +apollo-reporting-protobuf@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/apollo-reporting-protobuf/-/apollo-reporting-protobuf-0.8.0.tgz#ae9d967934d3d8ed816fc85a0d8068ef45c371b9" + integrity sha512-B3XmnkH6Y458iV6OsA7AhfwvTgeZnFq9nPVjbxmLKnvfkEl8hYADtz724uPa0WeBiD7DSFcnLtqg9yGmCkBohg== + dependencies: + "@apollo/protobufjs" "1.2.2" + +apollo-server-caching@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/apollo-server-caching/-/apollo-server-caching-0.7.0.tgz#e6d1e68e3bb571cba63a61f60b434fb771c6ff39" + integrity sha512-MsVCuf/2FxuTFVhGLK13B+TZH9tBd2qkyoXKKILIiGcZ5CDUEBO14vIV63aNkMkS1xxvK2U4wBcuuNj/VH2Mkw== + dependencies: + lru-cache "^6.0.0" + +apollo-server-core@^2.25.3: + version "2.25.3" + resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.25.3.tgz#1a649fd14b3928f5b6e65f0002b380fcfde56862" + integrity sha512-Midow3uZoJ9TjFNeCNSiWElTVZlvmB7G7tG6PPoxIR9Px90/v16Q6EzunDIO0rTJHRC3+yCwZkwtf8w2AcP0sA== + dependencies: + "@apollographql/apollo-tools" "^0.5.0" + "@apollographql/graphql-playground-html" "1.6.27" + "@apollographql/graphql-upload-8-fork" "^8.1.3" + "@josephg/resolvable" "^1.0.0" + "@types/ws" "^7.0.0" + apollo-cache-control "^0.14.0" + apollo-datasource "^0.9.0" + apollo-graphql "^0.9.0" + apollo-reporting-protobuf "^0.8.0" + apollo-server-caching "^0.7.0" + apollo-server-env "^3.1.0" + apollo-server-errors "^2.5.0" + apollo-server-plugin-base "^0.13.0" + apollo-server-types "^0.9.0" + apollo-tracing "^0.15.0" + async-retry "^1.2.1" + fast-json-stable-stringify "^2.0.0" + graphql-extensions "^0.15.0" + graphql-tag "^2.11.0" + graphql-tools "^4.0.8" + loglevel "^1.6.7" + lru-cache "^6.0.0" + sha.js "^2.4.11" + subscriptions-transport-ws "^0.9.19" + uuid "^8.0.0" + +apollo-server-env@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-3.1.0.tgz#0733c2ef50aea596cc90cf40a53f6ea2ad402cd0" + integrity sha512-iGdZgEOAuVop3vb0F2J3+kaBVi4caMoxefHosxmgzAbbSpvWehB8Y1QiSyyMeouYC38XNVk5wnZl+jdGSsWsIQ== + dependencies: + node-fetch "^2.6.1" + util.promisify "^1.0.0" + +apollo-server-errors@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.5.0.tgz#5d1024117c7496a2979e3e34908b5685fe112b68" + integrity sha512-lO5oTjgiC3vlVg2RKr3RiXIIQ5pGXBFxYGGUkKDhTud3jMIhs+gel8L8zsEjKaKxkjHhCQAA/bcEfYiKkGQIvA== + +apollo-server-express@^2.25.3: + version "2.25.3" + resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.25.3.tgz#33fe0dae27fa71c8710e714efd93451bf2eb105f" + integrity sha512-tTFYn0oKH2qqLwVj7Ez2+MiKleXACODiGh5IxsB7VuYCPMAi9Yl8iUSlwTjQUvgCWfReZjnf0vFL2k5YhDlrtQ== + dependencies: + "@apollographql/graphql-playground-html" "1.6.27" + "@types/accepts" "^1.3.5" + "@types/body-parser" "1.19.0" + "@types/cors" "2.8.10" + "@types/express" "^4.17.12" + "@types/express-serve-static-core" "^4.17.21" + accepts "^1.3.5" + apollo-server-core "^2.25.3" + apollo-server-types "^0.9.0" + body-parser "^1.18.3" + cors "^2.8.5" + express "^4.17.1" + graphql-subscriptions "^1.0.0" + graphql-tools "^4.0.8" + parseurl "^1.3.2" + subscriptions-transport-ws "^0.9.19" + type-is "^1.6.16" + +apollo-server-plugin-base@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.13.0.tgz#3f85751a420d3c4625355b6cb3fbdd2acbe71f13" + integrity sha512-L3TMmq2YE6BU6I4Tmgygmd0W55L+6XfD9137k+cWEBFu50vRY4Re+d+fL5WuPkk5xSPKd/PIaqzidu5V/zz8Kg== + dependencies: + apollo-server-types "^0.9.0" + +apollo-server-types@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/apollo-server-types/-/apollo-server-types-0.9.0.tgz#ccf550b33b07c48c72f104fbe2876232b404848b" + integrity sha512-qk9tg4Imwpk732JJHBkhW0jzfG0nFsLqK2DY6UhvJf7jLnRePYsPxWfPiNkxni27pLE2tiNlCwoDFSeWqpZyBg== + dependencies: + apollo-reporting-protobuf "^0.8.0" + apollo-server-caching "^0.7.0" + apollo-server-env "^3.1.0" + +apollo-server@^2.18.2: + version "2.25.3" + resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.25.3.tgz#2e5db9ce5217389625ac5014551dcbdeeedcd1d8" + integrity sha512-+eUY2//DLkU7RkJLn6CTl1P89/ZMHuUQnWqv8La2iJ2hLT7Me+nMx+hgHl3LqlT/qDstQ8qA45T85FuCayplmQ== + dependencies: + apollo-server-core "^2.25.3" + apollo-server-express "^2.25.3" + express "^4.0.0" + graphql-subscriptions "^1.0.0" + graphql-tools "^4.0.8" + stoppable "^1.1.0" + +apollo-tracing@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.15.0.tgz#237fbbbf669aee4370b7e9081b685eabaa8ce84a" + integrity sha512-UP0fztFvaZPHDhIB/J+qGuy6hWO4If069MGC98qVs0I8FICIGu4/8ykpX3X3K6RtaQ56EDAWKykCxFv4ScxMeA== + dependencies: + apollo-server-env "^3.1.0" + apollo-server-plugin-base "^0.13.0" + +apollo-upload-client@14.1.2: + version "14.1.2" + resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-14.1.2.tgz#7a72b000f1cd67eaf8f12b4bda2796d0898c0dae" + integrity sha512-ozaW+4tnVz1rpfwiQwG3RCdCcZ93RV/37ZQbRnObcQ9mjb+zur58sGDPVg9Ef3fiujLmiE/Fe9kdgvIMA3VOjA== + dependencies: + "@apollo/client" "^3.1.5" + "@babel/runtime" "^7.11.2" + extract-files "^9.0.0" + +apollo-utilities@^1.0.1, apollo-utilities@^1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.4.tgz#6129e438e8be201b6c55b0f13ce49d2c7175c9cf" + integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig== + dependencies: + "@wry/equality" "^0.1.2" + fast-json-stable-stringify "^2.0.0" + ts-invariant "^0.4.0" + tslib "^1.10.0" + +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argsarray@0.0.1, argsarray@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb" + integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs= + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-uniq@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= + +array.prototype.map@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.3.tgz#1609623618d3d84134a37d4a220030c2bd18420b" + integrity sha512-nNcb30v0wfDyIe26Yif3PcV1JXQp4zEeEfupG7L4SRjnD6HLbO5b2a7eVSba53bOx4YCHYMBHt+Fp4vYstneRA== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.5" + +asap@~2.0.3, asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +asmcrypto.js@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz#b9f84bd0a1fb82f21f8c29cc284a707ad17bba2e" + integrity sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA== + +asn1.js@^5.0.1, asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assemblyscript@0.19.10: + version "0.19.10" + resolved "https://registry.yarnpkg.com/assemblyscript/-/assemblyscript-0.19.10.tgz#7ede6d99c797a219beb4fa4614c3eab9e6343c8e" + integrity sha512-HavcUBXB3mBTRGJcpvaQjmnmaqKHBGREjSPNsIvnAk2f9dj78y4BkMaSSdvBQYWcDDzsHQjyUC8stICFkD1Odg== + dependencies: + binaryen "101.0.0-nightly.20210723" + long "^4.0.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@^1.0.0, async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async-retry@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" + integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== + dependencies: + retry "0.13.1" + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +async@^2.6.1, async@^2.6.2, async@^2.6.3: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + +await-semaphore@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/await-semaphore/-/await-semaphore-0.1.3.tgz#2b88018cc8c28e06167ae1cdff02504f1f9688d3" + integrity sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +axios@^0.21.1, axios@^0.21.2: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@6.26.1, babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-polyfill-corejs2@^0.1.4: + version "0.1.10" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz#a2c5c245f56c0cac3dbddbf0726a46b24f0f81d1" + integrity sha512-DO95wD4g0A8KRaHKi0D51NdGXzvpqVLnLu5BTvDlpqUEpTmeEtypgC1xqesORaWmiUOQI14UHKlzNd9iZ2G3ZA== + dependencies: + "@babel/compat-data" "^7.13.0" + "@babel/helper-define-polyfill-provider" "^0.1.5" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.1.3: + version "0.1.7" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz#80449d9d6f2274912e05d9e182b54816904befd0" + integrity sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.1.5" + core-js-compat "^3.8.1" + +babel-plugin-polyfill-regenerator@^0.1.2: + version "0.1.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.1.6.tgz#0fe06a026fe0faa628ccc8ba3302da0a6ce02f3f" + integrity sha512-OUrYG9iKPKz8NxswXbRAdSwF0GhRdIEMTloQATJi4bDuFqrXaXcCUT/VGNrr8pBcjMh1RxZ7Xt9cytVJTJfvMg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.1.5" + +babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: + version "7.0.0-beta.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== + +babel-polyfill@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + integrity sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM= + dependencies: + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" + +babel-preset-fbjs@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.3.0.tgz#a6024764ea86c8e06a22d794ca8b69534d263541" + integrity sha512-7QTLTCd2gwB2qGoi5epSULMHugSVgpcVt5YAeiFO9ABLrutDQzKfGwzxgZHLpugq8qMdg/DhRZDZ5CLKxBkEbw== + dependencies: + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.0.0" + "@babel/plugin-syntax-class-properties" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-block-scoped-functions" "^7.0.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.0.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" + "@babel/plugin-transform-for-of" "^7.0.0" + "@babel/plugin-transform-function-name" "^7.0.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-member-expression-literals" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/plugin-transform-object-super" "^7.0.0" + "@babel/plugin-transform-parameters" "^7.0.0" + "@babel/plugin-transform-property-literals" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-template-literals" "^7.0.0" + babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@6.26.0, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@6.18.0, babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +backo2@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bignumber.js@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" + integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== + +bignumber.js@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +binary-install-raw@0.0.13: + version "0.0.13" + resolved "https://registry.yarnpkg.com/binary-install-raw/-/binary-install-raw-0.0.13.tgz#43a13c6980eb9844e2932eb7a91a56254f55b7dd" + integrity sha512-v7ms6N/H7iciuk6QInon3/n2mu7oRX+6knJ9xFPsJ3rQePgAqcR3CRTwUheFd8SLbiq4LL7Z4G/44L9zscdt9A== + dependencies: + axios "^0.21.1" + rimraf "^3.0.2" + tar "^6.1.0" + +binaryen@101.0.0-nightly.20210723: + version "101.0.0-nightly.20210723" + resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-101.0.0-nightly.20210723.tgz#b6bb7f3501341727681a03866c0856500eec3740" + integrity sha512-eioJNqhHlkguVSbblHOtLqlhtC882SOEPKmNFZaDuz1hzQjolxZ+eu3/kaS10n3sGPONsIZsO7R9fR00UyhEUA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw== + dependencies: + safe-buffer "^5.0.1" + +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +bl@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f" + integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ== + dependencies: + readable-stream "^3.0.1" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +bluebird@^3.4.7, bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + +bn.js@4.11.8: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.3: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +body-parser@1.20.1, body-parser@^1.16.0, body-parser@^1.18.3: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +borc@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/borc/-/borc-2.1.2.tgz#6ce75e7da5ce711b963755117dd1b187f6f8cf19" + integrity sha512-Sy9eoUi4OiKzq7VovMn246iTo17kzuyHJKomCfpWMlI6RpfN1gk95w7d7gH264nApVLg0HZfcpz62/g4VH1Y4w== + dependencies: + bignumber.js "^9.0.0" + buffer "^5.5.0" + commander "^2.15.0" + ieee754 "^1.1.13" + iso-url "~0.4.7" + json-text-sequence "~0.1.0" + readable-stream "^3.6.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserslist@^4.14.5, browserslist@^4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" + integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== + dependencies: + caniuse-lite "^1.0.30001181" + colorette "^1.2.1" + electron-to-chromium "^1.3.649" + escalade "^3.1.1" + node-releases "^1.1.70" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== + +buffer-from@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" + integrity sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ== + +buffer-from@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^5.0.5, buffer@^5.2.1, buffer@^5.4.2, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0, buffer@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bufferutil@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== + dependencies: + node-gyp-build "^4.2.0" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== + +busboy@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" + integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== + dependencies: + dicer "0.3.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" + integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== + dependencies: + pascal-case "^3.1.1" + tslib "^1.10.0" + +camel-case@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30001181: + version "1.0.30001197" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001197.tgz#47ad15b977d2f32b3ec2fe2b087e0c50443771db" + integrity sha512-8aE+sqBqtXz4G8g35Eg/XEaFr2N7rd/VQ6eABGBmNtcB8cN6qNJhMi6oSFy4UWWZgqgL3filHT8Nha4meu3tsw== + +caseless@^0.12.0, caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +cbor@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +chalk@1.1.3, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@3.0.0, chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +change-case@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.2.tgz#fd48746cce02f03f0a672577d1d3a8dc2eceb037" + integrity sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA== + dependencies: + camel-case "^3.0.0" + constant-case "^2.0.0" + dot-case "^2.1.0" + header-case "^1.0.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + no-case "^2.3.2" + param-case "^2.1.0" + pascal-case "^2.0.0" + path-case "^2.1.0" + sentence-case "^2.1.0" + snake-case "^2.1.0" + swap-case "^1.1.0" + title-case "^2.1.0" + upper-case "^1.1.1" + upper-case-first "^1.1.0" + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +cheerio-select-tmp@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz#55bbef02a4771710195ad736d5e346763ca4e646" + integrity sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ== + dependencies: + css-select "^3.1.2" + css-what "^4.0.0" + domelementtype "^2.1.0" + domhandler "^4.0.0" + domutils "^2.4.4" + +cheerio@0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.20.0.tgz#5c710f2bab95653272842ba01c6ea61b3545ec35" + integrity sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "~3.8.1" + lodash "^4.1.0" + optionalDependencies: + jsdom "^7.0.2" + +cheerio@1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + integrity sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.5" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.5.tgz#88907e1828674e8f9fee375188b27dadd4f0fa2f" + integrity sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw== + dependencies: + cheerio-select-tmp "^0.1.0" + dom-serializer "~1.2.0" + domhandler "^4.0.0" + entities "~2.1.0" + htmlparser2 "^6.0.0" + parse5 "^6.0.0" + parse5-htmlparser2-tree-adapter "^6.0.0" + +chokidar@3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + +chownr@^1.0.1, chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +cids@^0.7.1, cids@~0.7.0, cids@~0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cids@~0.8.0: + version "0.8.3" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.8.3.tgz#aaf48ac8ed857c3d37dad94d8db1d8c9407b92db" + integrity sha512-yoXTbV3llpm+EBGWKeL9xKtksPE/s6DPoDSY4fn8I8TEW1zehWXPSB0pwAXVDlLaOlrw+sNynj995uD9abmPhA== + dependencies: + buffer "^5.6.0" + class-is "^1.1.0" + multibase "^1.0.0" + multicodec "^1.0.1" + multihashes "^1.0.1" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" + integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== + +cli-spinners@^2.2.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + +cli-table3@~0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== + dependencies: + object-assign "^4.1.0" + string-width "^2.1.1" + optionalDependencies: + colors "^1.1.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-buffer@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone-stats@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= + +clone@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" + integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs= + +clone@^1.0.0, clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +clone@^2.0.0, clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-logger@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/color-logger/-/color-logger-0.0.3.tgz#d9b22dd1d973e166b18bf313f9f481bba4df2018" + integrity sha1-2bIt0dlz4Waxi/MT+fSBu6TfIBg= + +color-logger@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/color-logger/-/color-logger-0.0.6.tgz#e56245ef29822657110c7cb75a9cd786cb69ed1b" + integrity sha1-5WJF7ymCJlcRDHy3WpzXhstp7Rs= + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + +colors@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" + integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== + +colors@^1.1.2, colors@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + +commander@^2.15.0, commander@^2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.1.tgz#f3b80acf9e1f48e3875c0688b41b6c31602eea1c" + integrity sha1-87gKz54fSOOHXAaItBtsMWAu6hw= + dependencies: + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" + +concat-stream@^1.6.0, concat-stream@^1.6.2, concat-stream@~1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +"concat-stream@github:hugomrdias/concat-stream#feat/smaller": + version "2.0.0" + resolved "https://codeload.github.com/hugomrdias/concat-stream/tar.gz/057bc7b5d6d8df26c8cf00a3f151b6721a0a8034" + dependencies: + inherits "^2.0.3" + readable-stream "^3.0.2" + +configstore@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-4.0.0.tgz#5933311e95d3687efb592c528b922d9262d227e7" + integrity sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ== + dependencies: + dot-prop "^4.1.0" + graceful-fs "^4.1.2" + make-dir "^1.0.0" + unique-string "^1.0.0" + write-file-atomic "^2.0.0" + xdg-basedir "^3.0.0" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +constant-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" + integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY= + dependencies: + snake-case "^2.1.0" + upper-case "^1.1.1" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@1.X, convert-source-map@^1.5.1, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +core-js-compat@^3.8.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.1.tgz#4e572acfe90aff69d76d8c37759d21a5c59bb455" + integrity sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA== + dependencies: + browserslist "^4.16.3" + semver "7.0.0" + +core-js-pure@^3.10.2: + version "3.19.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.19.1.tgz#edffc1fc7634000a55ba05e95b3f0fe9587a5aa4" + integrity sha512-Q0Knr8Es84vtv62ei6/6jXH/7izKmOrtrxH9WJTHLCMAVeU+8TF8z8Nr08CsH4Ot0oJKzBzJJL9SJBYIv7WlfQ== + +core-js@^2.4.0, core-js@^2.5.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.1, cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@3.0.6, cross-fetch@^3.0.4: + version "3.0.6" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== + dependencies: + node-fetch "2.6.1" + +cross-fetch@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-1.1.1.tgz#dede6865ae30f37eae62ac90ebb7bdac002b05a0" + integrity sha512-+VJE04+UfxxmBfcnmAu/lKor53RUCx/1ilOti4p+JgrnLQ4AZZIRoe2OEd76VaHyWQmQxqKnV+TaqjHC4r0HWw== + dependencies: + node-fetch "1.7.3" + whatwg-fetch "2.0.3" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== + +crypto-random-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= + +css-select@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8" + integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA== + dependencies: + boolbase "^1.0.0" + css-what "^4.0.0" + domhandler "^4.0.0" + domutils "^2.4.3" + nth-check "^2.0.0" + +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +css-what@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233" + integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A== + +css@2.X: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + +cssfilter@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" + integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4= + +cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +"cssstyle@>= 0.2.29 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + integrity sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ= + dependencies: + cssom "0.3.x" + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + +dataloader@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" + integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== + +debug-fabulous@0.0.X: + version "0.0.4" + resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-0.0.4.tgz#fa071c5d87484685424807421ca4b16b0b1a0763" + integrity sha1-+gccXYdIRoVCSAdCHKSxawsaB2M= + dependencies: + debug "2.X" + lazy-debug-legacy "0.0.X" + object-assign "4.1.0" + +debug@2.6.9, debug@2.X, debug@^2.2.0, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debug@^3.1.0, debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== + dependencies: + clone "^1.0.2" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +deferred-leveldown@~5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.0.1.tgz#1642eb18b535dfb2b6ac4d39fb10a9cbcfd13b09" + integrity sha512-BXohsvTedWOLkj2n/TY+yqVlrCWa2Zs8LSxh3uCAgFOru7/pjxKyZAexGa1j83BaKloER4PqUyQ9rGPJLt9bqA== + dependencies: + abstract-leveldown "~6.0.0" + inherits "^2.0.3" + +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +delimit-stream@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/delimit-stream/-/delimit-stream-0.1.0.tgz#9b8319477c0e5f8aeb3ce357ae305fc25ea1cd2b" + integrity sha512-a02fiQ7poS5CnjiJBAsjGLPp5EwVoGHNeu9sziBd9huppRfsAFIpv5zNLv0V1gbop53ilngAf5Kf331AwcoRBQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +deprecated-decorator@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" + integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-newline@2.X: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +dicer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" + +diff@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +docker-compose@0.23.4: + version "0.23.4" + resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.4.tgz#43bcabcde55a6ba2873b52fe0ccd99dd8fdceba8" + integrity sha512-yWdXby9uQ8o4syOfvoSJ9ZlTnLipvUmDn59uaYY5VGIUSUAfMPPGqE1DE3pOCnfSg9Tl9UOOFO0PCSAzuIHmuA== + +docker-modem@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-1.0.9.tgz#a1f13e50e6afb6cf3431b2d5e7aac589db6aaba8" + integrity sha512-lVjqCSCIAUDZPAZIeyM125HXfNvOmYYInciphNrLrylUtKyW66meAjSPXWchKVzoIYZx69TPnAepVSSkeawoIw== + dependencies: + JSONStream "1.3.2" + debug "^3.2.6" + readable-stream "~1.0.26-4" + split-ca "^1.0.0" + +dockerode@2.5.8: + version "2.5.8" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-2.5.8.tgz#1b661e36e1e4f860e25f56e0deabe9f87f1d0acc" + integrity sha512-+7iOUYBeDTScmOmQqpUYQaE7F4vvIt6+gIZNHWhqAQEI887tiPFB9OvXI/HzQYqfUNvukMK+9myLW63oTJPZpw== + dependencies: + concat-stream "~1.6.2" + docker-modem "^1.0.8" + tar-fs "~1.16.3" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@^1.0.1, dom-serializer@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" + integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + entities "^2.0.0" + +dom-serializer@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1, domelementtype@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" + integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== + +domhandler@2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + integrity sha1-LeWaCCLVAn+r/28DLCsloqir5zg= + dependencies: + domelementtype "1" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domhandler@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" + integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== + dependencies: + domelementtype "^2.1.0" + +domutils@1.5, domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^2.4.3, domutils@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" + integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.0.1" + domhandler "^4.0.0" + +dot-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" + integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4= + dependencies: + no-case "^2.2.0" + +dot-prop@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" + integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ== + dependencies: + is-obj "^1.0.0" + +double-ended-queue@2.1.0-0: + version "2.1.0-0" + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" + integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= + +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g== + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexify@^3.2.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +ejs@^2.6.1: + version "2.7.4" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" + integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + +electron-to-chromium@^1.3.649: + version "1.3.683" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.683.tgz#2c9ab53ff5275cf3dd49278af714d0f8975204f7" + integrity sha512-8mFfiAesXdEdE0DhkMKO7W9U6VU/9T3VTWwZ+4g84/YMP4kgwgFtQgUxuu7FUMcvSeKSNhFQNU+WZ68BQTLT5A== + +elliptic@6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.4.1.tgz#abe9d3297389ba424ac87e53d1c701962ce7433d" + integrity sha512-r4eRSeStEGf6M5SKdrQhhLK5bOwOBxQhIE3YSTnZE3GpKiLfnnhE+tPtrJE79+eDJgm39BM6LSoI8SCx4HbwlQ== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +end-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/end-stream/-/end-stream-0.1.0.tgz#32003f3f438a2b0143168137f8fa6e9866c81ed5" + integrity sha1-MgA/P0OKKwFDFoE3+PpumGbIHtU= + dependencies: + write-stream "~0.4.3" + +enquirer@2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.4.tgz#c608f2e1134c7f68c1c9ee056de13f9b31076de9" + integrity sha512-pkYrrDZumL2VS6VBGDhqbajCM2xpkUNLuKfGPjfKaSIBKYopQbqEFyrOkRMIb2HDR/rO1kGhEt/5twBwtzKBXw== + dependencies: + ansi-colors "^3.2.1" + +entities@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" + integrity sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY= + +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + +err-code@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" + integrity sha512-CJAN+O0/yA1CKfRn9SXOGctSpEM7DCon/r/5r2eXFMY2zCCJBasFhcM5I+1kh3Ap11FsQCX+vGHceNPvpWKhoA== + +err-code@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0-next.1, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: + version "1.18.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" + integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.2" + is-string "^1.0.5" + object-inspect "^1.9.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.0" + +es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-get-iterator@^1.0.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" + integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.0" + has-symbols "^1.0.1" + is-arguments "^1.1.0" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.5" + isarray "^2.0.5" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-denodeify@^0.1.1: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-denodeify/-/es6-denodeify-0.1.5.tgz#31d4d5fe9c5503e125460439310e16a2a3f39c1f" + integrity sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8= + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== + dependencies: + es6-promise "^4.0.3" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@1.0.3, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.6.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esdoc@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esdoc/-/esdoc-1.1.0.tgz#07d40ebf791764cd537929c29111e20a857624f3" + integrity sha512-vsUcp52XJkOWg9m1vDYplGZN2iDzvmjDL5M/Mp8qkoDG3p2s0yIQCIjKR5wfPBaM3eV14a6zhQNYiNTCVzPnxA== + dependencies: + babel-generator "6.26.1" + babel-traverse "6.26.0" + babylon "6.18.0" + cheerio "1.0.0-rc.2" + color-logger "0.0.6" + escape-html "1.0.3" + fs-extra "5.0.0" + ice-cap "0.0.4" + marked "0.3.19" + minimist "1.2.0" + taffydb "2.7.3" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eth-block-tracker@^4.4.2: + version "4.4.3" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz#766a0a0eb4a52c867a28328e9ae21353812cf626" + integrity sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw== + dependencies: + "@babel/plugin-transform-runtime" "^7.5.5" + "@babel/runtime" "^7.5.5" + eth-query "^2.1.0" + json-rpc-random-id "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.0: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-errors@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" + integrity sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-json-rpc-errors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz#c1965de0301fe941c058e928bebaba2e1285e3c4" + integrity sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-lib@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca" + integrity sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco= + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@0.2.8, eth-lib@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-rpc-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" + integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== + dependencies: + fast-safe-stringify "^2.0.6" + +ethereum-bloom-filters@^1.0.6: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" + integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-ens@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" + integrity sha512-a8cBTF4AWw1Q1Y37V1LSCS9pRY4Mh3f8vCg5cbXCCEJ3eno1hbI/+Ccv9SZLISYpqQhaglP3Bxb/34lS4Qf7Bg== + dependencies: + bluebird "^3.4.7" + eth-ens-namehash "^2.0.0" + js-sha3 "^0.5.7" + pako "^1.0.4" + underscore "^1.8.3" + web3 "^1.0.0-beta.34" + +ethereum-protocol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz#b7d68142f4105e0ae7b5e178cf42f8d4dc4b93cf" + integrity sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg== + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-tx@^1.0.0, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-tx@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.0.2: + version "7.0.9" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.9.tgz#2038baeb30f370a3e576ec175bd70bbbb6807d42" + integrity sha512-cRqvYYKJoitq6vMKMf8pXeVwvTrX+dRD0JwHaYqm8jvogK14tqIoCWH/KUHcRwnVxVXEYF/o6pup5jRG4V0xzg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-util@^7.1.0: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-1.0.1.tgz#664a4bcacfc1291ca2703de066df1178938dba1c" + integrity sha512-3Z5g1hG1das0JWU6cQ9HWWTY2nt9nXCcwj7eXVNAHKbo00XAZO8+NHlwdgXDWrL0SXVQMvTWN8Q/82DRH/JhPw== + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^7.0.2" + randombytes "^2.0.6" + scrypt-js "^3.0.1" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@^4.0.32: + version "4.0.48" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.48.tgz#330c65b8133e112b0613156e57e92d9009d8fbbe" + integrity sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g== + dependencies: + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.5.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@3.1.2, eventemitter3@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +eventsource@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^3.0.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" + integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + +explain-error@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/explain-error/-/explain-error-1.0.4.tgz#a793d3ac0cad4c6ab571e9968fbbab6cb2532929" + integrity sha512-/wSgNMxFusiYRy1rd19LT2SQlIXDppHpumpWo06wxjflD1OYxDLbl6rMVw+U3bxD5Nuhex4TKqv9Aem4D0lVzQ== + +express@^4.0.0, express@^4.14.0, express@^4.17.1: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= + dependencies: + is-extglob "^1.0.0" + +extract-files@9.0.0, extract-files@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" + integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +eyes@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +faker@^5.3.1: + version "5.4.0" + resolved "https://registry.yarnpkg.com/faker/-/faker-5.4.0.tgz#f18e55993c6887918182b003d163df14daeb3011" + integrity sha512-Y9n/Ky/xZx/Bj8DePvXspUYRtHl/rGQytoIT5LaxmNwSe3wWyOeOXb3lT6Dpipq240PVpeFaGKzScz/5fvff2g== + +fast-check@^2.12.1: + version "2.13.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.13.0.tgz#92a50a6a39b58760d4b0b52b12f98f28a9f020f6" + integrity sha512-IOfzKm/SCA+jpUEgAfqAuxHYPmgtmpnnwljQmYPRGrqYczcTKApXKHza/SNxFxYkecWfZilYa0DJdBvqz1bcSw== + dependencies: + pure-rand "^4.1.1" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-future@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fast-future/-/fast-future-1.0.2.tgz#8435a9aaa02d79248d17d704e76259301d99280a" + integrity sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo= + +fast-glob@^3.1.1: + version "3.2.5" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" + integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== + +fastq@^1.6.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" + integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== + dependencies: + reusify "^1.0.4" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165" + integrity sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg== + dependencies: + cross-fetch "^3.0.4" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + +fetch-cookie@0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.10.1.tgz#5ea88f3d36950543c87997c27ae2aeafb4b5c4d4" + integrity sha512-beB+VEd4cNeVG1PY+ee74+PkuCQnik78pgLi5Ah/7qdUfov8IctU0vLUbBT8/10Ma5GMBeI4wtxhGrEfKNYs2g== + dependencies: + tough-cookie "^2.3.3 || ^3.0.1 || ^4.0.0" + +fetch-cookie@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.7.0.tgz#a6fc137ad8363aa89125864c6451b86ecb7de802" + integrity sha512-Mm5pGlT3agW6t71xVM7vMZPIvI7T4FaTuFW4jari6dVzYHFDb3WZZsGpN22r/o3XMdkM0E7sPd1EGeyVbH2Tgg== + dependencies: + es6-denodeify "^0.1.1" + tough-cookie "^2.3.1" + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +first-chunk-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" + integrity sha1-Wb+1DNkF9g18OUzT2ayqtOatk04= + +flat@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" + integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== + dependencies: + is-buffer "~2.0.3" + +flatmap@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/flatmap/-/flatmap-0.0.3.tgz#1f18a4d938152d495965f9c958d923ab2dd669b4" + integrity sha512-OuR+o7kHVe+x9RtIujPay7Uw3bvDZBZFSBXClEphZuSDLmZTqMdclasf4vFSsogC8baDz0eaC2NdO/2dlXHBKQ== + +follow-redirects@^1.12.1: + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== + +follow-redirects@^1.14.0: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +foreach@^2.0.4, foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@^2.2.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-capacitor@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-2.0.4.tgz#5a22e72d40ae5078b4fe64fe4d08c0d3fc88ad3c" + integrity sha512-8S4f4WsCryNw2mJJchi46YgB6CR5Ze+4L1h8ewl9tEpL4SJ3ZO+c/bS4BWhB8bK+O3TMqhuZarTitd0S0eh2pA== + +fs-capacitor@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" + integrity sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" + integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^1.0.0" + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-jetpack@^2.2.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/fs-jetpack/-/fs-jetpack-2.4.0.tgz#6080c4ab464a019d37a404baeb47f32af8835026" + integrity sha512-S/o9Dd7K9A7gicVU32eT8G0kHcmSu0rCVdP79P0MWInKFb8XpTc8Syhoo66k9no+HDshtlh4pUJTws8X+8fdFQ== + dependencies: + minimatch "^3.0.2" + rimraf "^2.6.3" + +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-params@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/get-params/-/get-params-0.1.2.tgz#bae0dfaba588a0c60d7834c0d8dc2ff60eeef2fe" + integrity sha1-uuDfq6WIoMYNeDTA2Nwv9g7u8v4= + +get-port@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0, get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + +glob-parent@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.1.0, glob-parent@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-stream@^5.3.2: + version "5.3.5" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-5.3.5.tgz#a55665a9a8ccdc41915a87c701e32d4e016fad22" + integrity sha1-pVZlqajM3EGRWofHAeMtTgFvrSI= + dependencies: + extend "^3.0.0" + glob "^5.0.3" + glob-parent "^3.0.0" + micromatch "^2.3.7" + ordered-read-streams "^0.3.0" + through2 "^0.6.0" + to-absolute-glob "^0.1.1" + unique-stream "^2.0.2" + +glob@7.1.6, glob@^7.1.1: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^5.0.3: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +globby@11.0.2: + version "11.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" + integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +gluegun@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/gluegun/-/gluegun-4.6.1.tgz#f2a65d20378873de87a2143b8c3939ffc9a9e2b6" + integrity sha512-Jd5hV1Uku2rjBg59mYA/bnwLwynK7u9A1zmK/LIb/p5d3pzjDCKRjWFuxZXyPwl9rsvKGhJUQxkFo2HEy8crKQ== + dependencies: + apisauce "^2.0.1" + app-module-path "^2.2.0" + cli-table3 "~0.5.0" + colors "^1.3.3" + cosmiconfig "6.0.0" + cross-spawn "^7.0.0" + ejs "^2.6.1" + enquirer "2.3.4" + execa "^3.0.0" + fs-jetpack "^2.2.2" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.lowercase "^4.3.0" + lodash.lowerfirst "^4.3.1" + lodash.pad "^4.5.1" + lodash.padend "^4.6.1" + lodash.padstart "^4.6.1" + lodash.repeat "^4.1.0" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.trim "^4.5.1" + lodash.trimend "^4.5.1" + lodash.trimstart "^4.5.1" + lodash.uppercase "^4.3.0" + lodash.upperfirst "^4.3.1" + ora "^4.0.0" + pluralize "^8.0.0" + ramdasauce "^2.1.0" + semver "^7.0.0" + which "^2.0.0" + yargs-parser "^16.1.0" + +"gluegun@https://github.com/edgeandnode/gluegun#v4.3.1-pin-colors-dep": + version "4.3.1" + resolved "https://github.com/edgeandnode/gluegun#b34b9003d7bf556836da41b57ef36eb21570620a" + dependencies: + apisauce "^1.0.1" + app-module-path "^2.2.0" + cli-table3 "~0.5.0" + colors "1.3.3" + cosmiconfig "6.0.0" + cross-spawn "^7.0.0" + ejs "^2.6.1" + enquirer "2.3.4" + execa "^3.0.0" + fs-jetpack "^2.2.2" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.lowercase "^4.3.0" + lodash.lowerfirst "^4.3.1" + lodash.pad "^4.5.1" + lodash.padend "^4.6.1" + lodash.padstart "^4.6.1" + lodash.repeat "^4.1.0" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.trim "^4.5.1" + lodash.trimend "^4.5.1" + lodash.trimstart "^4.5.1" + lodash.uppercase "^4.3.0" + lodash.upperfirst "^4.3.1" + ora "^4.0.0" + pluralize "^8.0.0" + ramdasauce "^2.1.0" + semver "^7.0.0" + which "^2.0.0" + yargs-parser "^16.1.0" + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.9: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +graphql-extensions@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.15.0.tgz#3f291f9274876b0c289fa4061909a12678bd9817" + integrity sha512-bVddVO8YFJPwuACn+3pgmrEg6I8iBuYLuwvxiE+lcQQ7POotVZxm2rgGw0PvVYmWWf3DT7nTVDZ5ROh/ALp8mA== + dependencies: + "@apollographql/apollo-tools" "^0.5.0" + apollo-server-env "^3.1.0" + apollo-server-types "^0.9.0" + +graphql-subscriptions@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz#2142b2d729661ddf967b7388f7cf1dd4cf2e061d" + integrity sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g== + dependencies: + iterall "^1.3.0" + +graphql-tag@^2.11.0: + version "2.12.6" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" + integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== + dependencies: + tslib "^2.1.0" + +graphql-tag@^2.12.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.1.tgz#b065ef885e4800e4afd0842811b718a205f4aa58" + integrity sha512-LPewEE1vzGkHnCO8zdOGogKsHHBdtpGyihow1UuMwp6RnZa0lAS7NcbvltLOuo4pi5diQCPASAXZkQq44ffixA== + dependencies: + tslib "^1.14.1" + +graphql-tools@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.8.tgz#e7fb9f0d43408fb0878ba66b522ce871bafe9d30" + integrity sha512-MW+ioleBrwhRjalKjYaLQbr+920pHBgy9vM/n47sswtns8+96sRn5M/G+J1eu7IMeKWiN/9p6tmwCHU7552VJg== + dependencies: + apollo-link "^1.2.14" + apollo-utilities "^1.0.1" + deprecated-decorator "^0.1.6" + iterall "^1.1.3" + uuid "^3.1.0" + +graphql-tools@^6.2.4: + version "6.2.6" + resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-6.2.6.tgz#557c6d32797a02988f214bd596dec2abd12425dd" + integrity sha512-OyhSvK5ALVVD6bFiWjAqv2+lRyvjIRfb6Br5Tkjrv++rxnXDodPH/zhMbDGRw+W3SD5ioGEEz84yO48iPiN7jA== + dependencies: + "@graphql-tools/batch-delegate" "^6.2.6" + "@graphql-tools/code-file-loader" "^6.2.4" + "@graphql-tools/delegate" "^6.2.4" + "@graphql-tools/git-loader" "^6.2.4" + "@graphql-tools/github-loader" "^6.2.4" + "@graphql-tools/graphql-file-loader" "^6.2.4" + "@graphql-tools/graphql-tag-pluck" "^6.2.4" + "@graphql-tools/import" "^6.2.4" + "@graphql-tools/json-file-loader" "^6.2.4" + "@graphql-tools/links" "^6.2.4" + "@graphql-tools/load" "^6.2.4" + "@graphql-tools/load-files" "^6.2.4" + "@graphql-tools/merge" "^6.2.4" + "@graphql-tools/mock" "^6.2.4" + "@graphql-tools/module-loader" "^6.2.4" + "@graphql-tools/relay-operation-optimizer" "^6.2.4" + "@graphql-tools/resolvers-composition" "^6.2.4" + "@graphql-tools/schema" "^6.2.4" + "@graphql-tools/stitch" "^6.2.4" + "@graphql-tools/url-loader" "^6.2.4" + "@graphql-tools/utils" "^6.2.4" + "@graphql-tools/wrap" "^6.2.4" + tslib "~2.0.1" + +graphql-upload@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/graphql-upload/-/graphql-upload-11.0.0.tgz#24b245ff18f353bab6715e8a055db9fd73035e10" + integrity sha512-zsrDtu5gCbQFDWsNa5bMB4nf1LpKX9KDgh+f8oL1288ijV4RxeckhVozAjqjXAfRpxOHD1xOESsh6zq8SjdgjA== + dependencies: + busboy "^0.3.1" + fs-capacitor "^6.1.0" + http-errors "^1.7.3" + isobject "^4.0.0" + object-path "^0.11.4" + +graphql-ws@4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-4.1.5.tgz#03526b29acb54a424a9fbe300a4bd69ff65a50b3" + integrity sha512-yUQ1AjegD1Y9jDS699kyw7Mw+9H+rILm2HoS8N5a5B5YTH93xy3yifFhAJpKGc2wb/8yGdlVy8gTcud0TPqi6Q== + +graphql@15.5.0, graphql@^15.3.0: + version "15.5.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" + integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +gulp-sourcemaps@^1.5.2: + version "1.12.1" + resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.12.1.tgz#b437d1f3d980cf26e81184823718ce15ae6597b6" + integrity sha1-tDfR89mAzyboEYSCNxjOFa5ll7Y= + dependencies: + "@gulp-sourcemaps/map-sources" "1.X" + acorn "4.X" + convert-source-map "1.X" + css "2.X" + debug-fabulous "0.0.X" + detect-newline "2.X" + graceful-fs "4.X" + source-map "~0.6.0" + strip-bom "2.X" + through2 "2.X" + vinyl "1.X" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-bigints@^1.0.0, has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.0, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-symbols@^1.0.1, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.0, he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +header-case@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" + integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.3" + +hi-base32@~0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/hi-base32/-/hi-base32-0.5.1.tgz#1279f2ddae2673219ea5870c2121d2a33132857e" + integrity sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA== + +highlight.js@^10.4.0, highlight.js@^10.4.1: + version "10.6.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.6.0.tgz#0073aa71d566906965ba6e1b7be7b2682f5e18b6" + integrity sha512-8mlRcn5vk/r4+QcqerapwBYTe+iPL5ih6xrNylxrnBdHQiijDETfXX7VIxC3UiCRiINBJfANBAsPzAvRQj8RpQ== + +highlightjs-solidity@^1.0.21: + version "1.0.21" + resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-1.0.21.tgz#6d257215b5b635231d4d0c523f2c419bbff6fe42" + integrity sha512-ozOtTD986CBIxuIuauzz2lqCOTpd27TbfYm+msMtNSB69mJ0cdFNvZ6rOO5iFtEHtDkVYVEFQywXffG2sX3XTw== + +hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +htmlparser2@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.1.tgz#422521231ef6d42e56bd411da8ba40aa36e91446" + integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.4.4" + entities "^2.0.0" + +htmlparser2@~3.8.1: + version "3.8.3" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" + integrity sha1-mWwosZFRaovoZQGn15dX5ccMEGg= + dependencies: + domelementtype "1" + domhandler "2.3" + domutils "1.5" + entities "1.0" + readable-stream "1.1" + +http-basic@^8.1.1: + version "8.1.3" + resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" + integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== + dependencies: + caseless "^0.12.0" + concat-stream "^1.6.2" + http-response-object "^3.0.1" + parse-cache-control "^1.0.1" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@^1.7.3: + version "1.8.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" + integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-response-object@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810" + integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA== + dependencies: + "@types/node" "^10.0.3" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +ice-cap@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/ice-cap/-/ice-cap-0.0.4.tgz#8a6d31ab4cac8d4b56de4fa946df3352561b6e18" + integrity sha1-im0xq0ysjUtW3k+pRt8zUlYbbhg= + dependencies: + cheerio "0.20.0" + color-logger "0.0.3" + +iconv-lite@0.4.24, iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +immediate@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + +immediate@3.3.0, immediate@^3.2.2, immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +immutable@3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" + integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= + +immutable@~3.7.6: + version "3.7.6" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" + integrity sha1-E7TTyxK++hVIKib+Gy665kAHHks= + +import-fresh@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-from@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== + dependencies: + resolve-from "^5.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +ip-regex@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== + +ip-regex@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + +ip@^1.1.5: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" + integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipfs-block@~0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/ipfs-block/-/ipfs-block-0.8.1.tgz#05e1068832775e8f1c2da5b64106cc837fd2acb9" + integrity sha512-0FaCpmij+jZBoUYhjoB5ptjdl9QzvrdRIoBmUU5JiBnK2GA+4YM/ifklaB8ePRhA/rRzhd+KYBjvMFMAL4NrVQ== + dependencies: + cids "~0.7.0" + class-is "^1.1.0" + +ipfs-http-client@34.0.0: + version "34.0.0" + resolved "https://registry.yarnpkg.com/ipfs-http-client/-/ipfs-http-client-34.0.0.tgz#8804d06a11c22306332a8ffa0949b6f672a0c9c8" + integrity sha512-4RCkk8ix4Dqn6sxqFVwuXWCZ1eLFPsVaj6Ijvu1fs9VYgxgVudsW9PWwarlr4mw1xUCmPWYyXnEbGgzBrfMy0Q== + dependencies: + abort-controller "^3.0.0" + async "^2.6.1" + bignumber.js "^9.0.0" + bl "^3.0.0" + bs58 "^4.0.1" + buffer "^5.4.2" + cids "~0.7.1" + concat-stream "github:hugomrdias/concat-stream#feat/smaller" + debug "^4.1.0" + detect-node "^2.0.4" + end-of-stream "^1.4.1" + err-code "^2.0.0" + explain-error "^1.0.4" + flatmap "0.0.3" + glob "^7.1.3" + ipfs-block "~0.8.1" + ipfs-utils "~0.0.3" + ipld-dag-cbor "~0.15.0" + ipld-dag-pb "~0.17.3" + ipld-raw "^4.0.0" + is-ipfs "~0.6.1" + is-pull-stream "0.0.0" + is-stream "^2.0.0" + iso-stream-http "~0.1.2" + iso-url "~0.4.6" + iterable-ndjson "^1.1.0" + just-kebab-case "^1.1.0" + just-map-keys "^1.1.0" + kind-of "^6.0.2" + ky "^0.11.2" + ky-universal "^0.2.2" + lru-cache "^5.1.1" + multiaddr "^6.0.6" + multibase "~0.6.0" + multicodec "~0.5.1" + multihashes "~0.4.14" + ndjson "github:hugomrdias/ndjson#feat/readable-stream3" + once "^1.4.0" + peer-id "~0.12.3" + peer-info "~0.15.1" + promise-nodeify "^3.0.1" + promisify-es6 "^1.0.3" + pull-defer "~0.2.3" + pull-stream "^3.6.9" + pull-to-stream "~0.1.1" + pump "^3.0.0" + qs "^6.5.2" + readable-stream "^3.1.1" + stream-to-pull-stream "^1.7.2" + tar-stream "^2.0.1" + through2 "^3.0.1" + +ipfs-utils@~0.0.3: + version "0.0.4" + resolved "https://registry.yarnpkg.com/ipfs-utils/-/ipfs-utils-0.0.4.tgz#946114cfeb6afb4454b4ccb10d2327cd323b0cce" + integrity sha512-7cZf6aGj2FG3XJWhCNwn4mS93Q0GEWjtBZvEHqzgI43U2qzNDCyzfS1pei1Y5F+tw/zDJ5U4XG0G9reJxR53Ig== + dependencies: + buffer "^5.2.1" + is-buffer "^2.0.3" + is-electron "^2.2.0" + is-pull-stream "0.0.0" + is-stream "^2.0.0" + kind-of "^6.0.2" + readable-stream "^3.4.0" + +ipld-dag-cbor@~0.15.0: + version "0.15.3" + resolved "https://registry.yarnpkg.com/ipld-dag-cbor/-/ipld-dag-cbor-0.15.3.tgz#283afdb81d5b07db8e4fff7a10ef5e517e87f299" + integrity sha512-m23nG7ZyoVFnkK55/bLAErc7EfiMgaEQlqHWDTGzPI+O5r6bPfp+qbL5zTVSIT8tpbHmu174dwerVtLoVgeVyA== + dependencies: + borc "^2.1.2" + buffer "^5.5.0" + cids "~0.8.0" + is-circular "^1.0.2" + multicodec "^1.0.0" + multihashing-async "~0.8.0" + +ipld-dag-pb@~0.17.3: + version "0.17.4" + resolved "https://registry.yarnpkg.com/ipld-dag-pb/-/ipld-dag-pb-0.17.4.tgz#080841cfdd014d996f8da7f3a522ec8b1f6b6494" + integrity sha512-YwCxETEMuXVspOKOhjIOHJvKvB/OZfCDkpSFiYBQN2/JQjM9y/RFCYzIQGm0wg7dCFLrhvfjAZLTSaKs65jzWA== + dependencies: + cids "~0.7.0" + class-is "^1.1.0" + multicodec "~0.5.1" + multihashing-async "~0.7.0" + protons "^1.0.1" + stable "~0.1.8" + +ipld-raw@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/ipld-raw/-/ipld-raw-4.0.1.tgz#49a6f58cdfece5a4d581925b19ee19255be2a29d" + integrity sha512-WjIdtZ06jJEar8zh+BHB84tE6ZdbS/XNa7+XCArOYfmeJ/c01T9VQpeMwdJQYn5c3s5UvvCu7y4VIi3vk2g1bA== + dependencies: + cids "~0.7.0" + multicodec "^1.0.0" + multihashing-async "~0.8.0" + +is-arguments@^1.0.4, is-arguments@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@^2.0.3, is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + +is-circular@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-circular/-/is-circular-1.0.2.tgz#2e0ab4e9835f4c6b0ea2b9855a84acd501b8366c" + integrity sha512-YttjnrswnUYRVJvxCvu8z+PGMUSzC2JttP0OEXezlAEdp3EXzhf7IZ3j0gRAybJBQupedIZFhY61Tga6E0qASA== + +is-core-module@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-electron@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.1.tgz#751b1dd8a74907422faa5c35aaa0cf66d98086e9" + integrity sha512-r8EEQQsqT+Gn0aXFx7lTFygYQhILLCB+wn0WCDL5LZRINeLH/Rvw1j2oKodELLXYNImQ3CRlVsY8wW4cGOsyuw== + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-function@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b" + integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ== + +is-glob@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab" + integrity sha512-9MTn0dteHETtyUx8pxqMwg5hMBi3pvlyglJ+b79KOCca0po23337LbVV2Hl4xmMvfw++ljnO0/+5G6G+0Szh6g== + dependencies: + ip-regex "^2.0.0" + +is-ip@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" + integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== + dependencies: + ip-regex "^4.0.0" + +is-ipfs@~0.6.1: + version "0.6.3" + resolved "https://registry.yarnpkg.com/is-ipfs/-/is-ipfs-0.6.3.tgz#82a5350e0a42d01441c40b369f8791e91404c497" + integrity sha512-HyRot1dvLcxImtDqPxAaY1miO6WsiP/z7Yxpg2qpaLWv5UdhAPtLvHJ4kMLM0w8GSl8AFsVF23PHe1LzuWrUlQ== + dependencies: + bs58 "^4.0.1" + cids "~0.7.0" + mafmt "^7.0.0" + multiaddr "^7.2.1" + multibase "~0.6.0" + multihashes "~0.4.13" + +is-lower-case@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" + integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= + dependencies: + lower-case "^1.1.0" + +is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + +is-promise@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + +is-promise@~1, is-promise@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-1.0.1.tgz#31573761c057e33c2e91aab9e96da08cefbe76e5" + integrity sha512-mjWH5XxnhMA8cFnDchr6qRP9S/kLntKuEfIYku+PaN1CnS8v+OG9O/BKpRCVRJvpIkgAZm0Pf5Is3iSSOILlcg== + +is-pull-stream@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/is-pull-stream/-/is-pull-stream-0.0.0.tgz#a3bc3d1c6d3055151c46bde6f399efed21440ca9" + integrity sha512-NWLwqCc95I6m8FZDYLAmVJc9Xgk8O+8pPOoDKFTC293FH4S7FBcbLCw3WWPCdiT8uUSdzPy47VM08WPDMJJrag== + +is-regex@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.1" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + +is-stream@^1.0.0, is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-upper-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" + integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= + dependencies: + upper-case "^1.1.0" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-valid-glob@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-0.3.0.tgz#d4b55c69f51886f9b65c70d6c2622d37e29f48fe" + integrity sha1-1LVcafUYhvm2XHDWwmItN+KfSP4= + +is-weakref@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" + integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== + dependencies: + call-bind "^1.0.0" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +iso-random-stream@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iso-random-stream/-/iso-random-stream-1.1.2.tgz#c703da2c518db573277c5678cc43c5298283d64c" + integrity sha512-7y0tsBBgQs544iTYjyrMp5xvgrbYR8b+plQq1Bryp+03p0LssrxC9C1M0oHv4QESDt7d95c74XvMk/yawKqX+A== + dependencies: + buffer "^6.0.3" + readable-stream "^3.4.0" + +iso-stream-http@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/iso-stream-http/-/iso-stream-http-0.1.2.tgz#b3dfea4c9f23ff26d078d40c539cfc0dfebacd37" + integrity sha512-oHEDNOysIMTNypbg2f1SlydqRBvjl4ZbSE9+0awVxnkx3K2stGTFwB/kpVqnB6UEfF8QD36kAjDwZvqyXBLMnQ== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^3.1.1" + +iso-url@~0.4.6, iso-url@~0.4.7: + version "0.4.7" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-0.4.7.tgz#de7e48120dae46921079fe78f325ac9e9217a385" + integrity sha512-27fFRDnPAMnHGLq36bWTpKET+eiXct3ENlCcdcMdk+mjXrb2kw3mhBUg1B7ewAC0kVzlOPhADzQgz1SE6Tglog== + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + +isomorphic-ws@4.0.1, isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +iterable-ndjson@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/iterable-ndjson/-/iterable-ndjson-1.1.0.tgz#36f7e8a5bb04fd087d384f29e44fc4280fc014fc" + integrity sha512-OOp1Lb0o3k5MkXHx1YaIY5Z0ELosZfTnBaas9f8opJVcZGBIONA2zY/6CYE+LKkqrSDooIneZbrBGgOZnHPkrg== + dependencies: + string_decoder "^1.2.0" + +iterall@^1.1.3, iterall@^1.2.1, iterall@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== + +iterate-iterator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" + integrity sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw== + +iterate-value@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + +jayson@3.6.6: + version "3.6.6" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.6.6.tgz#189984f624e398f831bd2be8e8c80eb3abf764a1" + integrity sha512-f71uvrAWTtrwoww6MKcl9phQTC+56AopLyEenWvKVAIMz+q0oVGj6tenLZ7Z6UiPBkJtKLj4kt0tACllFQruGQ== + dependencies: + "@types/connect" "^3.4.33" + "@types/express-serve-static-core" "^4.17.9" + "@types/lodash" "^4.14.159" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + lodash "^4.17.20" + uuid "^8.3.2" + ws "^7.4.5" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@0.8.0, js-sha3@^0.8.0, js-sha3@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsan@^3.1.13: + version "3.1.13" + resolved "https://registry.yarnpkg.com/jsan/-/jsan-3.1.13.tgz#4de8c7bf8d1cfcd020c313d438f930cec4b91d86" + integrity sha512-9kGpCsGHifmw6oJet+y8HaCl14y7qgAsxVdV3pCHDySNR3BfDC30zgkssd7x5LRVAT22dnpbe9JdzzmXZnq9/g== + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + +jsdom@^7.0.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e" + integrity sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4= + dependencies: + abab "^1.0.0" + acorn "^2.4.0" + acorn-globals "^1.0.4" + cssom ">= 0.3.0 < 0.4.0" + cssstyle ">= 0.2.29 < 0.3.0" + escodegen "^1.6.1" + nwmatcher ">= 1.3.7 < 2.0.0" + parse5 "^1.5.1" + request "^2.55.0" + sax "^1.1.4" + symbol-tree ">= 3.1.0 < 4.0.0" + tough-cookie "^2.2.0" + webidl-conversions "^2.0.0" + whatwg-url-compat "~0.6.5" + xml-name-validator ">= 2.0.1 < 3.0.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-pointer@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.1.tgz#3c6caa6ac139e2599f5a1659d39852154015054d" + integrity sha512-3OvjqKdCBvH41DLpV4iSt6v2XhZXV1bPB4OROuknvUXI7ZQNofieCPkmE26stEJ9zdQuvIxDHCuYhfgxFAAs+Q== + dependencies: + foreach "^2.0.4" + +json-rpc-engine@^5.1.3: + version "5.4.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" + integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== + dependencies: + eth-rpc-errors "^3.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json-text-sequence@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.1.1.tgz#a72f217dc4afc4629fff5feb304dc1bd51a2f3d2" + integrity sha512-L3mEegEWHRekSHjc7+sc8eJhba9Clq1PZ8kMkzf8OxElhXc8O4TS5MwcVlj9aEbm5dr81N90WHC5nAz3UO971w== + dependencies: + delimit-stream "0.1.0" + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + +jsondown@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jsondown/-/jsondown-1.0.0.tgz#c5cc5cda65f515d2376136a104b5f535534f26e3" + integrity sha512-p6XxPaq59aXwcdDQV3ISMA5xk+1z6fJuctcwwSdR9iQgbYOcIrnknNrhcMGG+0FaUfKHGkdDpQNaZrovfBoyOw== + dependencies: + memdown "1.4.1" + mkdirp "0.5.1" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +just-kebab-case@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/just-kebab-case/-/just-kebab-case-1.1.0.tgz#ebe854fde84b0afa4e597fcd870b12eb3c026755" + integrity sha512-QkuwuBMQ9BQHMUEkAtIA4INLrkmnnveqlFB1oFi09gbU0wBdZo6tTnyxNWMR84zHxBuwK7GLAwqN8nrvVxOLTA== + +just-map-keys@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/just-map-keys/-/just-map-keys-1.2.1.tgz#ef6e16133b7d34329962dfae9101d581abb1b143" + integrity sha512-Dmyz1Cy2SWM+PpqDPB1kdDglyexdzMthnAsvOIE9w4OPj8NDRuY1mh20x/JfG5w6fCGw9F0WmcofJhYZ4MiuyA== + +keccak@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" + integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keypair@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/keypair/-/keypair-1.0.4.tgz#a749a45f388593f3950f18b3757d32a93bd8ce83" + integrity sha512-zwhgOhhniaL7oxMgUMKKw5219PWWABMO+dgMnzJOQ2/5L3XJtTJGhW2PEXlxXj9zaccdReZJZ83+4NPhVfNVDg== + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + +ky-universal@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.2.2.tgz#7a36e1a75641a98f878157463513965f799f5bfe" + integrity sha512-fb32o/fKy/ux2ALWa9HU2hvGtfOq7/vn2nH0FpVE+jwNzyTeORlAbj3Fiw+WLMbUlmVqZIWupnLZ2USHvqwZHw== + dependencies: + abort-controller "^3.0.0" + node-fetch "^2.3.0" + +ky@^0.11.2: + version "0.11.2" + resolved "https://registry.yarnpkg.com/ky/-/ky-0.11.2.tgz#4ffe6621d9d9ab61bf0f5500542e3a96d1ba0815" + integrity sha512-5Aou5BWue5/mkPqIRqzSWW+0Hkl403pr/2AIrCKYw7cVl/Xoe8Xe4KLBO0PRjbz7GnRe1/8wW1KhqQNFFE7/GQ== + +lazy-debug-legacy@0.0.X: + version "0.0.1" + resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" + integrity sha1-U3cWwHduTPeePtG2IfdljCkRsbE= + +lazystream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= + dependencies: + readable-stream "^2.0.5" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +level-codec@9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.1.tgz#042f4aa85e56d4328ace368c950811ba802b7247" + integrity sha512-ajFP0kJ+nyq4i6kptSM+mAvJKLOg1X5FiFPtLG9M5gCEZyBmgDi3FkDrvlMkEzrUn1cWxtvVmrvoS4ASyO/q+Q== + +level-codec@9.0.2, level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-js@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-js/-/level-js-4.0.2.tgz#fa51527fa38b87c4d111b0d0334de47fcda38f21" + integrity sha512-PeGjZsyMG4O89KHiez1zoMJxStnkM+oBIqgACjoo5PJqFiSUUm3GNod/KcbqN5ktyZa8jkG7I1T0P2u6HN9lIg== + dependencies: + abstract-leveldown "~6.0.1" + immediate "~3.2.3" + inherits "^2.0.3" + ltgt "^2.1.2" + typedarray-to-buffer "~3.1.5" + +level-packager@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level-write-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/level-write-stream/-/level-write-stream-1.0.0.tgz#3f7fbb679a55137c0feb303dee766e12ee13c1dc" + integrity sha1-P3+7Z5pVE3wP6zA97nZuEu4Twdw= + dependencies: + end-stream "~0.1.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +level@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/level/-/level-5.0.1.tgz#8528cc1ee37ac413270129a1eab938c610be3ccb" + integrity sha512-wcak5OQeA4rURGacqS62R/xNHjCYnJSQDBOlm4KNUGJVE9bWv2B04TclqReYejN+oD65PzD4FsqeWoI5wNC5Lg== + dependencies: + level-js "^4.0.0" + level-packager "^5.0.0" + leveldown "^5.0.0" + opencollective-postinstall "^2.0.0" + +leveldown@5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.0.2.tgz#c8edc2308c8abf893ffc81e66ab6536111cae92c" + integrity sha512-Ib6ygFYBleS8x2gh3C1AkVsdrUShqXpe6jSTnZ6sRycEXKhqVf+xOSkhgSnjidpPzyv0d95LJVFrYQ4NuXAqHA== + dependencies: + abstract-leveldown "~6.0.0" + fast-future "~1.0.2" + napi-macros "~1.8.1" + node-gyp-build "~3.8.0" + +leveldown@^5.0.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" + integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== + dependencies: + abstract-leveldown "~6.2.1" + napi-macros "~2.0.0" + node-gyp-build "~4.1.0" + +levelup@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.0.2.tgz#bcb8d28d0a82ee97f1c6d00f20ea6d32c2803c5b" + integrity sha512-cx9PmLENwbGA3svWBEbeO2HazpOSOYSXH4VA+ahVpYyurvD+SDSfURl29VBY2qgyk+Vfy2dJd71SBRckj/EZVA== + dependencies: + deferred-leveldown "~5.0.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + xtend "~4.0.0" + +levelup@4.4.0, levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +libp2p-crypto-secp256k1@~0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/libp2p-crypto-secp256k1/-/libp2p-crypto-secp256k1-0.3.1.tgz#4cbeb857f5cfe5fefb1253e6b2994420c0ca166e" + integrity sha512-evrfK/CeUSd/lcELUdDruyPBvxDmLairth75S32OLl3H+++2m2fV24JEtxzdFS9JH3xEFw0h6JFO8DBa1bP9dA== + dependencies: + async "^2.6.2" + bs58 "^4.0.1" + multihashing-async "~0.6.0" + nodeify "^1.0.1" + safe-buffer "^5.1.2" + secp256k1 "^3.6.2" + +libp2p-crypto@~0.16.1: + version "0.16.4" + resolved "https://registry.yarnpkg.com/libp2p-crypto/-/libp2p-crypto-0.16.4.tgz#fb1a4ba39d56789303947784b5b0d6cefce12fdc" + integrity sha512-II8HxKc9jbmQp34pprlluNxsBCWJDjHRPYJzuRy7ragztNip9Zb7uJ4lCje6gGzz4DNAcHkAUn+GqCIK1592iA== + dependencies: + asmcrypto.js "^2.3.2" + asn1.js "^5.0.1" + async "^2.6.1" + bn.js "^4.11.8" + browserify-aes "^1.2.0" + bs58 "^4.0.1" + iso-random-stream "^1.1.0" + keypair "^1.0.1" + libp2p-crypto-secp256k1 "~0.3.0" + multihashing-async "~0.5.1" + node-forge "^0.10.0" + pem-jwk "^2.0.0" + protons "^1.0.1" + rsa-pem-to-jwk "^1.1.3" + tweetnacl "^1.0.0" + ursa-optional "~0.10.0" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +linked-list@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/linked-list/-/linked-list-0.1.0.tgz#798b0ff97d1b92a4fd08480f55aea4e9d49d37bf" + integrity sha1-eYsP+X0bkqT9CEgPVa6k6dSdN78= + +load-json-file@^1.0.0, load-json-file@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash-es@^4.2.1: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + +lodash._reinterpolate@^3.0.0, lodash._reinterpolate@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.assign@^4.0.3, lodash.assign@^4.0.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= + +lodash.assignin@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= + +lodash.assigninwith@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assigninwith/-/lodash.assigninwith-4.2.0.tgz#af02c98432ac86d93da695b4be801401971736af" + integrity sha1-rwLJhDKshtk9ppW0voAUAZcXNq8= + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= + +lodash.flatmap@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" + integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4= + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + +lodash.isequal@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205" + integrity sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU= + +lodash.lowercase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz#46515aced4acb0b7093133333af068e4c3b14e9d" + integrity sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA== + +lodash.lowerfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz#de3c7b12e02c6524a0059c2f6cb7c5c52655a13d" + integrity sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.pad@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70" + integrity sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg== + +lodash.padend@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" + integrity sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw== + +lodash.padstart@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" + integrity sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw== + +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha1-o45GtzRp4EILDaEhLmbUFL42S6Q= + +lodash.repeat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lodash.repeat/-/lodash.repeat-4.1.0.tgz#fc7de8131d8c8ac07e4b49f74ffe829d1f2bec44" + integrity sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw== + +lodash.rest@^4.0.0: + version "4.0.5" + resolved "https://registry.yarnpkg.com/lodash.rest/-/lodash.rest-4.0.5.tgz#954ef75049262038c96d1fc98b28fdaf9f0772aa" + integrity sha1-lU73UEkmIDjJbR/Jiyj9r58Hcqo= + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + +lodash.sum@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b" + integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s= + +lodash.template@4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.2.4.tgz#d053c19e8e74e38d965bf4fb495d80f109e7f7a4" + integrity sha1-0FPBno50442WW/T7SV2A8Qnn96Q= + dependencies: + lodash._reinterpolate "~3.0.0" + lodash.assigninwith "^4.0.0" + lodash.keys "^4.0.0" + lodash.rest "^4.0.0" + lodash.templatesettings "^4.0.0" + lodash.tostring "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.tostring@^4.0.0: + version "4.1.4" + resolved "https://registry.yarnpkg.com/lodash.tostring/-/lodash.tostring-4.1.4.tgz#560c27d1f8eadde03c2cce198fef5c031d8298fb" + integrity sha1-Vgwn0fjq3eA8LM4Zj+9cAx2CmPs= + +lodash.trim@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/lodash.trim/-/lodash.trim-4.5.1.tgz#36425e7ee90be4aa5e27bcebb85b7d11ea47aa57" + integrity sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg== + +lodash.trimend@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/lodash.trimend/-/lodash.trimend-4.5.1.tgz#12804437286b98cad8996b79414e11300114082f" + integrity sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA== + +lodash.trimstart@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz#8ff4dec532d82486af59573c39445914e944a7f1" + integrity sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ== + +lodash.uppercase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz#c404abfd1469f93931f9bb24cf6cc7d57059bc73" + integrity sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA== + +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + +lodash.zipwith@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.zipwith/-/lodash.zipwith-4.2.0.tgz#afacf03fd2f384af29e263c3c6bda3b80e3f51fd" + integrity sha1-r6zwP9LzhK8p4mPDxr2juA4/Uf0= + +lodash@4.17.21, lodash@^4.1.0, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.1: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +log-symbols@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + +loglevel@^1.6.7: + version "1.7.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" + integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +looper@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/looper/-/looper-3.0.0.tgz#2efa54c3b1cbaba9b94aee2e5914b0be57fbb749" + integrity sha512-LJ9wplN/uSn72oJRsXTx+snxPet5c8XiZmOKCm906NVYu+ag6SB6vUcnJcWxgnl2NfbIyeobAn7Bwv6xRj2XJg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case-first@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" + integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= + dependencies: + lower-case "^1.1.2" + +lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +ltgt@2.2.1, ltgt@^2.1.2, ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +mafmt@^6.0.2: + version "6.0.10" + resolved "https://registry.yarnpkg.com/mafmt/-/mafmt-6.0.10.tgz#3ad251c78f14f8164e66f70fd3265662da41113a" + integrity sha512-FjHDnew6dW9lUu3eYwP0FvvJl9uvNbqfoJM+c1WJcSyutNEIlyu6v3f/rlPnD1cnmue38IjuHlhBdIh3btAiyw== + dependencies: + multiaddr "^6.1.0" + +mafmt@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/mafmt/-/mafmt-7.1.0.tgz#4126f6d0eded070ace7dbbb6fb04977412d380b5" + integrity sha512-vpeo9S+hepT3k2h5iFxzEHvvR0GPBx9uKaErmnRzYNcaKb03DgOArjEMlgG4a9LcuZZ89a3I8xbeto487n26eA== + dependencies: + multiaddr "^7.3.0" + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +map-stream@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.6.tgz#d2ef4eb811a28644c7a8989985c69c2fdd496827" + integrity sha1-0u9OuBGihkTHqJiZhcacL91JaCc= + +marked@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790" + integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg== + +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memdown@1.4.1, memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= + dependencies: + readable-stream "^2.0.1" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^2.3.7: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.46.0: + version "1.46.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" + integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@^2.1.16: + version "2.1.29" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" + integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== + dependencies: + mime-db "1.46.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +"minimatch@2 || 3", minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minipass@^3.0.0: + version "3.3.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" + integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== + dependencies: + yallist "^4.0.0" + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*, mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mkdirp@^0.5.0, mkdirp@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@8.1.2: + version "8.1.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.1.2.tgz#d67fad13300e4f5cd48135a935ea566f96caf827" + integrity sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.4.2" + debug "4.1.1" + diff "4.0.2" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "3.14.0" + log-symbols "4.0.0" + minimatch "3.0.4" + ms "2.1.2" + object.assign "4.1.0" + promise.allsettled "1.0.2" + serialize-javascript "4.0.0" + strip-json-comments "3.0.1" + supports-color "7.1.0" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.0.0" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.1" + +mock-fs@^4.1.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.13.0.tgz#31c02263673ec3789f90eb7b6963676aa407a598" + integrity sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA== + +module@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/module/-/module-1.2.5.tgz#b503eb06cdc13473f56818426974cde7ec59bf15" + integrity sha1-tQPrBs3BNHP1aBhCaXTN5+xZvxU= + dependencies: + chalk "1.1.3" + concat-stream "1.5.1" + lodash.template "4.2.4" + map-stream "0.0.6" + tildify "1.2.0" + vinyl-fs "2.4.3" + yargs "4.6.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multiaddr@^6.0.3, multiaddr@^6.0.6, multiaddr@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/multiaddr/-/multiaddr-6.1.1.tgz#9aae57b3e399089b9896d9455afa8f6b117dff06" + integrity sha512-Q1Ika0F9MNhMtCs62Ue+GWIJtRFEhZ3Xz8wH7/MZDVZTWhil1/H2bEGN02kUees3hkI3q1oHSjmXYDM0gxaFjQ== + dependencies: + bs58 "^4.0.1" + class-is "^1.1.0" + hi-base32 "~0.5.0" + ip "^1.1.5" + is-ip "^2.0.0" + varint "^5.0.0" + +multiaddr@^7.2.1, multiaddr@^7.3.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/multiaddr/-/multiaddr-7.5.0.tgz#976c88e256e512263445ab03b3b68c003d5f485e" + integrity sha512-GvhHsIGDULh06jyb6ev+VfREH9evJCFIRnh3jUt9iEZ6XDbyoisZRFEI9bMvK/AiR6y66y6P+eoBw9mBYMhMvw== + dependencies: + buffer "^5.5.0" + cids "~0.8.0" + class-is "^1.1.0" + is-ip "^3.1.0" + multibase "^0.7.0" + varint "^5.0.0" + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@^1.0.0, multibase@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-1.0.1.tgz#4adbe1de0be8a1ab0274328b653c3f1903476724" + integrity sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5, multicodec@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0, multicodec@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.13, multihashes@~0.4.14, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +multihashes@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-1.0.1.tgz#a89415d68283cf6287c6e219e304e75ce7fb73fe" + integrity sha512-S27Tepg4i8atNiFaU5ZOm3+gl3KQlUanLs/jWcBxQHFttgq+5x1OgbQmf2d8axJ/48zYGBd/wT9d723USMFduw== + dependencies: + buffer "^5.6.0" + multibase "^1.0.1" + varint "^5.0.0" + +multihashing-async@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/multihashing-async/-/multihashing-async-0.5.2.tgz#4af40e0dde2f1dbb12a7c6b265181437ac26b9de" + integrity sha512-mmyG6M/FKxrpBh9xQDUvuJ7BbqT93ZeEeH5X6LeMYKoYshYLr9BDdCsvDtZvn+Egf+/Xi+aOznrWL4vp3s+p0Q== + dependencies: + blakejs "^1.1.0" + js-sha3 "~0.8.0" + multihashes "~0.4.13" + murmurhash3js "^3.0.1" + nodeify "^1.0.1" + +multihashing-async@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/multihashing-async/-/multihashing-async-0.6.0.tgz#c1fc6696a624b9bf39b160b0c4c4e7ba3f394453" + integrity sha512-Qv8pgg99Lewc191A5nlXy0bSd2amfqlafNJZmarU6Sj7MZVjpR94SCxQjf4DwPtgWZkiLqsjUQBXA2RSq+hYyA== + dependencies: + blakejs "^1.1.0" + js-sha3 "~0.8.0" + multihashes "~0.4.13" + murmurhash3js "^3.0.1" + nodeify "^1.0.1" + +multihashing-async@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multihashing-async/-/multihashing-async-0.7.0.tgz#3234fb98295be84386b85bfd20377d3e5be20d6b" + integrity sha512-SCbfl3f+DzJh+/5piukga9ofIOxwfT05t8R4jfzZIJ88YE9zU9+l3K2X+XB19MYyxqvyK9UJRNWbmQpZqQlbRA== + dependencies: + blakejs "^1.1.0" + buffer "^5.2.1" + err-code "^1.1.2" + js-sha3 "~0.8.0" + multihashes "~0.4.13" + murmurhash3js-revisited "^3.0.0" + +multihashing-async@~0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/multihashing-async/-/multihashing-async-0.8.2.tgz#3d5da05df27d83be923f6d04143a0954ff87f27f" + integrity sha512-2lKa1autuCy8x7KIEj9aVNbAb3aIMRFYIwN7mq/zD4pxgNIVgGlm+f6GKY4880EOF2Y3GktHYssRy7TAJQ2DyQ== + dependencies: + blakejs "^1.1.0" + buffer "^5.4.3" + err-code "^2.0.0" + js-sha3 "^0.8.0" + multihashes "^1.0.1" + murmurhash3js-revisited "^3.0.0" + +murmurhash3js-revisited@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/murmurhash3js-revisited/-/murmurhash3js-revisited-3.0.0.tgz#6bd36e25de8f73394222adc6e41fa3fac08a5869" + integrity sha512-/sF3ee6zvScXMb1XFJ8gDsSnY+X8PbOyjIuBhtgis10W2Jx4ZjIhikUCIF9c4gpJxVnQIsPAFrSwTCuAjicP6g== + +murmurhash3js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/murmurhash3js/-/murmurhash3js-3.0.1.tgz#3e983e5b47c2a06f43a713174e7e435ca044b998" + integrity sha512-KL8QYUaxq7kUbcl0Yto51rMcYt7E/4N4BG3/c96Iqw1PQrTRspu8Cpx4TZ4Nunib1d4bEkIH3gjCYlP2RLBdow== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nan@^2.12.1: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nan@^2.14.0, nan@^2.14.2: + version "2.16.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" + integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +nanoid@^2.0.0: + version "2.1.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" + integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== + +napi-macros@~1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-1.8.2.tgz#299265c1d8aa401351ad0675107d751228c03eda" + integrity sha512-Tr0DNY4RzTaBG2W2m3l7ZtFuJChTH6VZhXVhkGGjF/4cZTt+i8GcM9ozD+30Lmr4mDoZ5Xx34t2o4GJqYWDGcg== + +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + +"ndjson@github:hugomrdias/ndjson#feat/readable-stream3": + version "1.5.0" + resolved "https://codeload.github.com/hugomrdias/ndjson/tar.gz/4db16da6b42e5b39bf300c3a7cde62abb3fa3a11" + dependencies: + json-stringify-safe "^5.0.1" + minimist "^1.2.0" + split2 "^3.1.0" + through2 "^3.0.0" + +needle@^2.2.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.6.0.tgz#24dbb55f2509e2324b4a99d61f413982013ccdbe" + integrity sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +no-case@^2.2.0, no-case@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@1.7.3, node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + +node-fetch@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.4.1.tgz#b2e38f1117b8acbedbe0524f041fb3177188255d" + integrity sha512-P9UbpFK87NyqBZzUuDBDz4f6Yiys8xm8j7ACDbi6usvFm6KItklQUKjeoqTrYS/S1k6I8oaOC2YLLDr/gg26Mw== + +node-fetch@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-fetch@^2.3.0: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-fetch@^2.6.1: + version "2.6.6" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89" + integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-gyp-build@^4.2.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" + integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + +node-gyp-build@~3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.8.0.tgz#0f57efeb1971f404dfcbfab975c284de7c70f14a" + integrity sha512-bYbpIHyRqZ7sVWXxGpz8QIRug5JZc/hzZH4GbdT9HTZi6WmKCZ8GLvP8OZ9TTiIBvwPFKgtGrlWQSXDAvYdsPw== + +node-gyp-build@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" + integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-interval-tree@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/node-interval-tree/-/node-interval-tree-1.3.3.tgz#15ffb904cde08270214acace8dc7653e89ae32b7" + integrity sha512-K9vk96HdTK5fEipJwxSvIIqwTqr4e3HRJeJrNxBSeVMNSC/JWARRaX7etOLOuTmrRMeOI/K5TCJu3aWIwZiNTw== + dependencies: + shallowequal "^1.0.2" + +node-pre-gyp@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" + integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-releases@^1.1.70: + version "1.1.71" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" + integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== + +nodeify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nodeify/-/nodeify-1.0.1.tgz#64ab69a7bdbaf03ce107b4f0335c87c0b9e91b1d" + integrity sha512-n7C2NyEze8GCo/z73KdbjRsBiLbv6eBn1FxwYKQ23IqGo7pQY3mhQan61Sv7eEDJCiyUjTVrVkXTzJCo1dW7Aw== + dependencies: + is-promise "~1.0.0" + promise "~1.3.0" + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +noop-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf" + integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78= + +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1, normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== + dependencies: + boolbase "^1.0.0" + +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +nullthrows@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +"nwmatcher@>= 1.3.7 < 2.0.0": + version "1.4.4" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" + integrity sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + integrity sha1-ejs9DpgGPUP0wD8uiubNUahog6A= + +object-assign@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" + integrity sha512-CdsOUYIh5wIiozhJ3rLQgmUTgcyzFwZZrqhkKhODMoGtPKM+wt0h0CNIoauJWMsS9822EdzPsF/6mb4nLvPN5g== + +object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" + integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== + +object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object-path@^0.11.4: + version "0.11.8" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.8.tgz#ed002c02bbdd0070b78a27455e8ae01fc14d4742" + integrity sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.getownpropertydescriptors@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +oboe@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" + integrity sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY= + dependencies: + http-https "^1.0.0" + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha1-VVQoTFQ6ImbXo48X4HOCH73jk80= + dependencies: + http-https "^1.0.0" + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +opencollective-postinstall@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + +optimism@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.14.0.tgz#256fb079a3428585b40a3a8462f907e0abd2fc49" + integrity sha512-ygbNt8n4DOCVpkwiLF+IrKKeNHOjtr9aXLWGP9HNJGoblSGsnVbJLstcH6/nE9Xy5ZQtlkSioFQNnthmENW6FQ== + dependencies: + "@wry/context" "^0.5.2" + "@wry/trie" "^0.2.1" + +optimist@~0.3.5: + version "0.3.7" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9" + integrity sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ== + dependencies: + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +ora@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" + strip-ansi "^5.2.0" + wcwidth "^1.0.1" + +ora@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-4.1.1.tgz#566cc0348a15c36f5f0e979612842e02ba9dddbc" + integrity sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A== + dependencies: + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-spinners "^2.2.0" + is-interactive "^1.0.0" + log-symbols "^3.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +ordered-read-streams@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b" + integrity sha1-cTfmmzKYuzQiR6G77jiByA4v14s= + dependencies: + is-stream "^1.0.1" + readable-stream "^2.0.1" + +original-require@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" + integrity sha1-DxMEcVhM0zURxew4yNWSE/msXiA= + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + +p-limit@3.1.0, p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@^1.0.4: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +param-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-cache-control@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" + integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-headers@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" + integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5-htmlparser2-tree-adapter@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" + integrity sha1-m387DeMr543CQBsXVzzK8Pb1nZQ= + +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + +parse5@^6.0.0, parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@^1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^2.0.0, pascal-case@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e" + integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4= + dependencies: + camel-case "^3.0.0" + upper-case-first "^1.1.0" + +pascal-case@^3.1.1, pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5" + integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU= + dependencies: + no-case "^2.2.0" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pbkdf2@^3.0.17: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pbkdf2@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +peer-id@~0.12.2, peer-id@~0.12.3: + version "0.12.5" + resolved "https://registry.yarnpkg.com/peer-id/-/peer-id-0.12.5.tgz#b22a1edc5b4aaaa2bb830b265ba69429823e5179" + integrity sha512-3xVWrtIvNm9/OPzaQBgXDrfWNx63AftgFQkvqO6YSZy7sP3Fuadwwbn54F/VO9AnpyW/26i0WRQz9FScivXrmw== + dependencies: + async "^2.6.3" + class-is "^1.1.0" + libp2p-crypto "~0.16.1" + multihashes "~0.4.15" + +peer-info@~0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/peer-info/-/peer-info-0.15.1.tgz#21254a7c516d0dd046b150120b9aaf1b9ad02146" + integrity sha512-Y91Q2tZRC0CpSTPd1UebhGqniOrOAk/aj60uYUcWJXCoLTAnGu+4LJGoiay8ayudS6ice7l3SKhgL/cS62QacA== + dependencies: + mafmt "^6.0.2" + multiaddr "^6.0.3" + peer-id "~0.12.2" + unique-by "^1.0.0" + +pem-jwk@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pem-jwk/-/pem-jwk-2.0.0.tgz#1c5bb264612fc391340907f5c1de60c06d22f085" + integrity sha512-rFxu7rVoHgQ5H9YsP50dDWf0rHjreVA2z0yPiWr5WdH/UHb29hKtF7h6l8vNd1cbYR1t0QL+JKhW55a2ZV4KtA== + dependencies: + asn1.js "^5.0.1" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +picomatch@^2.0.5: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pkg-conf@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-1.1.3.tgz#378e56d6fd13e88bfb6f4a25df7a83faabddba5b" + integrity sha1-N45W1v0T6Iv7b0ol33qD+qvduls= + dependencies: + find-up "^1.0.0" + load-json-file "^1.1.0" + object-assign "^4.0.1" + symbol "^0.2.1" + +pkginfo@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" + integrity sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8= + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +pouchdb-abstract-mapreduce@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-7.2.2.tgz#dd1b10a83f8d24361dce9aaaab054614b39f766f" + integrity sha512-7HWN/2yV2JkwMnGnlp84lGvFtnm0Q55NiBUdbBcaT810+clCGKvhssBCrXnmwShD1SXTwT83aszsgiSfW+SnBA== + dependencies: + pouchdb-binary-utils "7.2.2" + pouchdb-collate "7.2.2" + pouchdb-collections "7.2.2" + pouchdb-errors "7.2.2" + pouchdb-fetch "7.2.2" + pouchdb-mapreduce-utils "7.2.2" + pouchdb-md5 "7.2.2" + pouchdb-utils "7.2.2" + +pouchdb-adapter-leveldb-core@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-adapter-leveldb-core/-/pouchdb-adapter-leveldb-core-7.2.2.tgz#e0aa6a476e2607d7ae89f4a803c9fba6e6d05a8a" + integrity sha512-K9UGf1Ivwe87mjrMqN+1D07tO/DfU7ariVDrGffuOjvl+3BcvUF25IWrxsBObd4iPOYCH7NVQWRpojhBgxULtQ== + dependencies: + argsarray "0.0.1" + buffer-from "1.1.1" + double-ended-queue "2.1.0-0" + levelup "4.4.0" + pouchdb-adapter-utils "7.2.2" + pouchdb-binary-utils "7.2.2" + pouchdb-collections "7.2.2" + pouchdb-errors "7.2.2" + pouchdb-json "7.2.2" + pouchdb-md5 "7.2.2" + pouchdb-merge "7.2.2" + pouchdb-utils "7.2.2" + sublevel-pouchdb "7.2.2" + through2 "3.0.2" + +pouchdb-adapter-memory@^7.1.1: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-adapter-memory/-/pouchdb-adapter-memory-7.2.2.tgz#c0ec2e87928d516ca9d1b5badc7269df6f95e5ea" + integrity sha512-9o+zdItPEq7rIrxdkUxgsLNaZkDJAGEqqoYgeYdrHidOCZnlhxhX3g7/R/HcpDKC513iEPqJWDJQSfeT6nVKkw== + dependencies: + memdown "1.4.1" + pouchdb-adapter-leveldb-core "7.2.2" + pouchdb-utils "7.2.2" + +pouchdb-adapter-node-websql@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-adapter-node-websql/-/pouchdb-adapter-node-websql-7.0.0.tgz#64ad88dd45b23578e454bf3032a3a79f9d1e4008" + integrity sha512-fNaOMO8bvMrRTSfmH4RSLSpgnKahRcCA7Z0jg732PwRbGvvMdGbreZwvKPPD1fg2tm2ZwwiXWK2G3+oXyoqZYw== + dependencies: + pouchdb-adapter-websql-core "7.0.0" + pouchdb-utils "7.0.0" + websql "1.0.0" + +pouchdb-adapter-utils@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-adapter-utils/-/pouchdb-adapter-utils-7.0.0.tgz#1ac8d34481911e0e9a9bf51024610a2e7351dc80" + integrity sha512-UWKPC6jkz6mHUzZefrU7P5X8ZGvBC8LSNZ7BIp0hWvJE6c20cnpDwedTVDpZORcCbVJpDmFOHBYnOqEIblPtbA== + dependencies: + pouchdb-binary-utils "7.0.0" + pouchdb-collections "7.0.0" + pouchdb-errors "7.0.0" + pouchdb-md5 "7.0.0" + pouchdb-merge "7.0.0" + pouchdb-utils "7.0.0" + +pouchdb-adapter-utils@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-adapter-utils/-/pouchdb-adapter-utils-7.2.2.tgz#c64426447d9044ba31517a18500d6d2d28abd47d" + integrity sha512-2CzZkTyTyHZkr3ePiWFMTiD5+56lnembMjaTl8ohwegM0+hYhRyJux0biAZafVxgIL4gnCUC4w2xf6WVztzKdg== + dependencies: + pouchdb-binary-utils "7.2.2" + pouchdb-collections "7.2.2" + pouchdb-errors "7.2.2" + pouchdb-md5 "7.2.2" + pouchdb-merge "7.2.2" + pouchdb-utils "7.2.2" + +pouchdb-adapter-websql-core@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-adapter-websql-core/-/pouchdb-adapter-websql-core-7.0.0.tgz#27b3e404159538e515b2567baa7869f90caac16c" + integrity sha512-NyMaH0bl20SdJdOCzd+fwXo8JZ15a48/MAwMcIbXzsRHE4DjFNlRcWAcjUP6uN4Ezc+Gx+r2tkBBMf71mIz1Aw== + dependencies: + pouchdb-adapter-utils "7.0.0" + pouchdb-binary-utils "7.0.0" + pouchdb-collections "7.0.0" + pouchdb-errors "7.0.0" + pouchdb-json "7.0.0" + pouchdb-merge "7.0.0" + pouchdb-utils "7.0.0" + +pouchdb-binary-utils@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-binary-utils/-/pouchdb-binary-utils-7.0.0.tgz#cb71a288b09572a231f6bab1b4aed201c4d219a7" + integrity sha512-yUktdOPIPvOVouCjJN3uop+bCcpdPwePrLm9eUAZNgEYnUFu0njdx7Q0WRsZ7UJ6l75HinL5ZHk4bnvEt86FLw== + dependencies: + buffer-from "1.1.0" + +pouchdb-binary-utils@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-binary-utils/-/pouchdb-binary-utils-7.2.2.tgz#0690b348052c543b1e67f032f47092ca82bcb10e" + integrity sha512-shacxlmyHbUrNfE6FGYpfyAJx7Q0m91lDdEAaPoKZM3SzAmbtB1i+OaDNtYFztXjJl16yeudkDb3xOeokVL3Qw== + dependencies: + buffer-from "1.1.1" + +pouchdb-collate@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-collate/-/pouchdb-collate-7.2.2.tgz#fc261f5ef837c437e3445fb0abc3f125d982c37c" + integrity sha512-/SMY9GGasslknivWlCVwXMRMnQ8myKHs4WryQ5535nq1Wj/ehpqWloMwxEQGvZE1Sda3LOm7/5HwLTcB8Our+w== + +pouchdb-collections@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-7.0.0.tgz#fd1f632337dc6301b0ff8649732ca79204e41780" + integrity sha512-DaoUr/vU24Q3gM6ghj0va9j/oBanPwkbhkvnqSyC3Dm5dgf5pculNxueLF9PKMo3ycApoWzHMh6N2N8KJbDU2Q== + +pouchdb-collections@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-7.2.2.tgz#aeed77f33322429e3f59d59ea233b48ff0e68572" + integrity sha512-6O9zyAYlp3UdtfneiMYuOCWdUCQNo2bgdjvNsMSacQX+3g8WvIoFQCYJjZZCpTttQGb+MHeRMr8m2U95lhJTew== + +pouchdb-debug@^7.1.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/pouchdb-debug/-/pouchdb-debug-7.2.1.tgz#f5f869f6113c12ccb97cddf5b0a32b6e0e67e961" + integrity sha512-eP3ht/AKavLF2RjTzBM6S9gaI2/apcW6xvaKRQhEdOfiANqerFuksFqHCal3aikVQuDO+cB/cw+a4RyJn/glBw== + dependencies: + debug "3.1.0" + +pouchdb-errors@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-errors/-/pouchdb-errors-7.0.0.tgz#4e2a5a8b82af20cbe5f9970ca90b7ec74563caa0" + integrity sha512-dTusY8nnTw4HIztCrNl7AoGgwvS1bVf/3/97hDaGc4ytn72V9/4dK8kTqlimi3UpaurohYRnqac0SGXYP8vgXA== + dependencies: + inherits "2.0.3" + +pouchdb-errors@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-errors/-/pouchdb-errors-7.2.2.tgz#80d811d65c766c9d20b755c6e6cc123f8c3c4792" + integrity sha512-6GQsiWc+7uPfgEHeavG+7wuzH3JZW29Dnrvz8eVbDFE50kVFxNDVm3EkYHskvo5isG7/IkOx7PV7RPTA3keG3g== + dependencies: + inherits "2.0.4" + +pouchdb-fetch@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-fetch/-/pouchdb-fetch-7.2.2.tgz#492791236d60c899d7e9973f9aca0d7b9cc02230" + integrity sha512-lUHmaG6U3zjdMkh8Vob9GvEiRGwJfXKE02aZfjiVQgew+9SLkuOxNw3y2q4d1B6mBd273y1k2Lm0IAziRNxQnA== + dependencies: + abort-controller "3.0.0" + fetch-cookie "0.10.1" + node-fetch "2.6.0" + +pouchdb-find@^7.0.0: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-find/-/pouchdb-find-7.2.2.tgz#1227afdd761812d508fe0794b3e904518a721089" + integrity sha512-BmFeFVQ0kHmDehvJxNZl9OmIztCjPlZlVSdpijuFbk/Fi1EFPU1BAv3kLC+6DhZuOqU/BCoaUBY9sn66pPY2ag== + dependencies: + pouchdb-abstract-mapreduce "7.2.2" + pouchdb-collate "7.2.2" + pouchdb-errors "7.2.2" + pouchdb-fetch "7.2.2" + pouchdb-md5 "7.2.2" + pouchdb-selector-core "7.2.2" + pouchdb-utils "7.2.2" + +pouchdb-json@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-json/-/pouchdb-json-7.0.0.tgz#d9860f66f27a359ac6e4b24da4f89b6909f37530" + integrity sha512-w0bNRu/7VmmCrFWMYAm62n30wvJJUT2SokyzeTyj3hRohj4GFwTRg1mSZ+iAmxgRKOFE8nzZstLG/WAB4Ymjew== + dependencies: + vuvuzela "1.0.3" + +pouchdb-json@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-json/-/pouchdb-json-7.2.2.tgz#b939be24b91a7322e9a24b8880a6e21514ec5e1f" + integrity sha512-3b2S2ynN+aoB7aCNyDZc/4c0IAdx/ir3nsHB+/RrKE9cM3QkQYbnnE3r/RvOD1Xvr6ji/KOCBie+Pz/6sxoaug== + dependencies: + vuvuzela "1.0.3" + +pouchdb-mapreduce-utils@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-7.2.2.tgz#13a46a3cc2a3f3b8e24861da26966904f2963146" + integrity sha512-rAllb73hIkU8rU2LJNbzlcj91KuulpwQu804/F6xF3fhZKC/4JQMClahk+N/+VATkpmLxp1zWmvmgdlwVU4HtQ== + dependencies: + argsarray "0.0.1" + inherits "2.0.4" + pouchdb-collections "7.2.2" + pouchdb-utils "7.2.2" + +pouchdb-md5@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-md5/-/pouchdb-md5-7.0.0.tgz#935dc6bb507a5f3978fb653ca5790331bae67c96" + integrity sha512-yaSJKhLA3QlgloKUQeb2hLdT3KmUmPfoYdryfwHZuPTpXIRKTnMQTR9qCIRUszc0ruBpDe53DRslCgNUhAyTNQ== + dependencies: + pouchdb-binary-utils "7.0.0" + spark-md5 "3.0.0" + +pouchdb-md5@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-md5/-/pouchdb-md5-7.2.2.tgz#415401acc5a844112d765bd1fb4e5d9f38fb0838" + integrity sha512-c/RvLp2oSh8PLAWU5vFBnp6ejJABIdKqboZwRRUrWcfGDf+oyX8RgmJFlYlzMMOh4XQLUT1IoaDV8cwlsuryZw== + dependencies: + pouchdb-binary-utils "7.2.2" + spark-md5 "3.0.1" + +pouchdb-merge@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-merge/-/pouchdb-merge-7.0.0.tgz#9f476ce7e32aae56904ad770ae8a1dfe14b57547" + integrity sha512-tci5u6NpznQhGcPv4ho1h0miky9rs+ds/T9zQ9meQeDZbUojXNaX1Jxsb0uYEQQ+HMqdcQs3Akdl0/u0mgwPGg== + +pouchdb-merge@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-merge/-/pouchdb-merge-7.2.2.tgz#940d85a2b532d6a93a6cab4b250f5648511bcc16" + integrity sha512-6yzKJfjIchBaS7Tusuk8280WJdESzFfQ0sb4jeMUNnrqs4Cx3b0DIEOYTRRD9EJDM+je7D3AZZ4AT0tFw8gb4A== + +pouchdb-selector-core@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-selector-core/-/pouchdb-selector-core-7.2.2.tgz#264d7436a8c8ac3801f39960e79875ef7f3879a0" + integrity sha512-XYKCNv9oiNmSXV5+CgR9pkEkTFqxQGWplnVhO3W9P154H08lU0ZoNH02+uf+NjZ2kjse7Q1fxV4r401LEcGMMg== + dependencies: + pouchdb-collate "7.2.2" + pouchdb-utils "7.2.2" + +pouchdb-utils@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pouchdb-utils/-/pouchdb-utils-7.0.0.tgz#48bfced6665b8f5a2b2d2317e2aa57635ed1e88e" + integrity sha512-1bnoX1KdZYHv9wicDIFdO0PLiVIMzNDUBUZ/yOJZ+6LW6niQCB8aCv09ZztmKfSQcU5nnN3fe656tScBgP6dOQ== + dependencies: + argsarray "0.0.1" + clone-buffer "1.0.0" + immediate "3.0.6" + inherits "2.0.3" + pouchdb-collections "7.0.0" + pouchdb-errors "7.0.0" + pouchdb-md5 "7.0.0" + uuid "3.2.1" + +pouchdb-utils@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-utils/-/pouchdb-utils-7.2.2.tgz#c17c4788f1d052b0daf4ef8797bbc4aaa3945aa4" + integrity sha512-XmeM5ioB4KCfyB2MGZXu1Bb2xkElNwF1qG+zVFbQsKQij0zvepdOUfGuWvLRHxTOmt4muIuSOmWZObZa3NOgzQ== + dependencies: + argsarray "0.0.1" + clone-buffer "1.0.0" + immediate "3.3.0" + inherits "2.0.4" + pouchdb-collections "7.2.2" + pouchdb-errors "7.2.2" + pouchdb-md5 "7.2.2" + uuid "8.1.0" + +pouchdb@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/pouchdb/-/pouchdb-7.1.1.tgz#f5f8dcd1fc440fb76651cb26f6fc5d97a39cd6ce" + integrity sha512-8bXWclixNJZqokvxGHRsG19zehSJiaZaz4dVYlhXhhUctz7gMcNTElHjPBzBdZlKKvt9aFDndmXN1VVE53Co8g== + dependencies: + argsarray "0.0.1" + buffer-from "1.1.0" + clone-buffer "1.0.0" + double-ended-queue "2.1.0-0" + fetch-cookie "0.7.0" + immediate "3.0.6" + inherits "2.0.3" + level "5.0.1" + level-codec "9.0.1" + level-write-stream "1.0.0" + leveldown "5.0.2" + levelup "4.0.2" + ltgt "2.2.1" + node-fetch "2.4.1" + readable-stream "1.0.33" + spark-md5 "3.0.0" + through2 "3.0.1" + uuid "3.2.1" + vuvuzela "1.0.3" + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= + +prettier@1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +promise-nodeify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/promise-nodeify/-/promise-nodeify-3.0.1.tgz#f0f5d9720ee9ec71dd2bfa92667be504c10229c2" + integrity sha512-ghsSuzZXJX8iO7WVec2z7GI+Xk/EyiD+JZK7AZKhUqYfpLa/Zs4ylUD+CwwnKlG6G3HnkUPMAi6PO7zeqGKssg== + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +promise.allsettled@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9" + integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg== + dependencies: + array.prototype.map "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + iterate-value "^1.0.0" + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +promise@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" + integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== + dependencies: + asap "~2.0.6" + +promise@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-1.3.0.tgz#e5cc9a4c8278e4664ffedc01c7da84842b040175" + integrity sha512-R9WrbTF3EPkVtWjp7B7umQGVndpsi+rsDAfrR4xAALQpFLa/+2OriecLhawxzvii2gd9+DZFwROWDuUUaqS5yA== + dependencies: + is-promise "~1" + +promisify-es6@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/promisify-es6/-/promisify-es6-1.0.3.tgz#b012668c4df3c965ce13daac2b3a4d1726a96346" + integrity sha512-N9iVG+CGJsI4b4ZGazjwLnxErD2d9Pe4DPvvXSxYA9tFNu8ymXME4Qs5HIQ0LMJpNM7zj+m0NlNnNeqFpKzqnA== + +prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +protocol-buffers-schema@^3.3.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" + integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== + +protons@^1.0.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/protons/-/protons-1.2.1.tgz#5f1e0db8b2139469cd1c3b4e332a4c2d95d0a218" + integrity sha512-2oqDyc/SN+tNcJf8XxrXhYL7sQn2/OMl8mSdD7NVGsWjMEmAbks4eDVnCyf0vAoRbBWyWTEXWk4D8XfuKVl3zg== + dependencies: + buffer "^5.5.0" + protocol-buffers-schema "^3.3.1" + signed-varint "^2.0.1" + varint "^5.0.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +psl@^1.1.28: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +psl@^1.1.33: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pull-defer@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/pull-defer/-/pull-defer-0.2.3.tgz#4ee09c6d9e227bede9938db80391c3dac489d113" + integrity sha512-/An3KE7mVjZCqNhZsr22k1Tx8MACnUnHZZNPSJ0S62td8JtYr/AiRG42Vz7Syu31SoTLUzVIe61jtT/pNdjVYA== + +pull-stream@^3.2.3, pull-stream@^3.6.9: + version "3.6.14" + resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.14.tgz#529dbd5b86131f4a5ed636fdf7f6af00781357ee" + integrity sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew== + +pull-to-stream@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pull-to-stream/-/pull-to-stream-0.1.1.tgz#fa2058528528e3542b81d6f17cbc42288508ff37" + integrity sha512-thZkMv6F9PILt9zdvpI2gxs19mkDrlixYKX6cOBxAW16i1NZH+yLAmF4r8QfJ69zuQh27e01JZP9y27tsH021w== + dependencies: + readable-stream "^3.1.1" + +pump@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" + integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +pure-rand@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-4.1.2.tgz#cbad2a3e3ea6df0a8d80d8ba204779b5679a5205" + integrity sha512-uLzZpQWfroIqyFWmX/pl0OL2JHJdoU3dbh0dvZ25fChHFJJi56J5oQZhW6QgbT2Llwh1upki84LnTwlZvsungA== + +qs@6.11.0, qs@^6.4.0, qs@^6.5.2: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +qs@~6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystring@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" + integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" + integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== + +ramda@^0.24.1: + version "0.24.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" + integrity sha512-HEm619G8PaZMfkqCa23qiOe7r3R0brPu7ZgOsgKUsnvLhd0qhc/vTjkUovomgPWa5ECBa08fJZixth9LaoBo5w== + +ramda@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" + integrity sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ== + +ramdasauce@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ramdasauce/-/ramdasauce-2.1.3.tgz#acb45ecc7e4fc4d6f39e19989b4a16dff383e9c2" + integrity sha512-Ml3CPim4SKwmg5g9UI77lnRSeKr/kQw7YhQ6rfdMcBYy6DMlwmkEwQqjygJ3OhxPR+NfFfpjKl3Tf8GXckaqqg== + dependencies: + ramda "^0.24.1" + +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-is@^16.7.0, react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@1.0.33: + version "1.0.33" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.33.tgz#3a360dd66c1b1d7fd4705389860eda1d0f61126c" + integrity sha1-OjYN1mwbHX/UcFOJhg7aHQ9hEmw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@1.1: + version "1.1.13" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" + integrity sha1-9u73ZPUUyJ4rniMUanW6EGdW0j4= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@1.1.14, readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.1, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.15, readable-stream@~1.0.26-4: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@~0.0.2: + version "0.0.4" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-0.0.4.tgz#f32d76e3fb863344a548d79923007173665b3b8d" + integrity sha1-8y124/uGM0SlSNeZIwBxc2ZbO40= + +readable-stream@~2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + +redux-cli-logger@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/redux-cli-logger/-/redux-cli-logger-2.1.0.tgz#7e546502a4b08c7fac4fe2faee2326a6326cb4a1" + integrity sha512-75mVsggAJRSykWy2qxdGI7osocDWvc3RCMeN93hlvS/FxgdRww12NaXslez+W6gBOrSJKO7W16V0IzuISSfCxg== + dependencies: + colors "^1.1.2" + +redux-devtools-core@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/redux-devtools-core/-/redux-devtools-core-0.2.1.tgz#4e43cbe590a1f18c13ee165d2d42e0bc77a164d8" + integrity sha512-RAGOxtUFdr/1USAvxrWd+Gq/Euzgw7quCZlO5TgFpDfG7rB5tMhZUrNyBjpzgzL2yMk0eHnPYIGm7NkIfRzHxQ== + dependencies: + get-params "^0.1.2" + jsan "^3.1.13" + lodash "^4.17.11" + nanoid "^2.0.0" + remotedev-serialize "^0.1.8" + +redux-devtools-instrument@^1.9.4: + version "1.10.0" + resolved "https://registry.yarnpkg.com/redux-devtools-instrument/-/redux-devtools-instrument-1.10.0.tgz#036caf79fa1e5f25ec4bae38a9af4f08c69e323a" + integrity sha512-X8JRBCzX2ADSMp+iiV7YQ8uoTNyEm0VPFPd4T854coz6lvRiBrFSqAr9YAS2n8Kzxx8CJQotR0QF9wsMM+3DvA== + dependencies: + lodash "^4.17.19" + symbol-observable "^1.2.0" + +redux-saga@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.0.0.tgz#acb8b3ed9180fecbe75f342011d75af3ac11045b" + integrity sha512-GvJWs/SzMvEQgeaw6sRMXnS2FghlvEGsHiEtTLpJqc/FHF3I5EE/B+Hq5lyHZ8LSoT2r/X/46uWvkdCnK9WgHA== + dependencies: + "@redux-saga/core" "^1.0.0" + +redux@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== + dependencies: + lodash "^4.2.1" + lodash-es "^4.2.1" + loose-envify "^1.1.0" + symbol-observable "^1.0.3" + +redux@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" + integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + +regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg= + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== + dependencies: + is-equal-shallow "^0.1.3" + +relay-compiler@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.1.0.tgz#fb4672cdbe9b54869a3a79759edd8c2d91609cbe" + integrity sha512-HPqc3N3tNgEgUH5+lTr5lnLbgnsZMt+MRiyS0uAVNhuPY2It0X1ZJG+9qdA3L9IqKFUNwVn6zTO7RArjMZbARQ== + dependencies: + "@babel/core" "^7.0.0" + "@babel/generator" "^7.5.0" + "@babel/parser" "^7.0.0" + "@babel/runtime" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + babel-preset-fbjs "^3.3.0" + chalk "^4.0.0" + fb-watchman "^2.0.0" + fbjs "^3.0.0" + glob "^7.1.1" + immutable "~3.7.6" + nullthrows "^1.1.1" + relay-runtime "10.1.0" + signedsource "^1.0.0" + yargs "^15.3.1" + +relay-runtime@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.1.0.tgz#4753bf36e95e8d862cef33608e3d98b4ed730d16" + integrity sha512-bxznLnQ1ST6APN/cFi7l0FpjbZVchWQjjhj9mAuJBuUqNNCh9uV+UTRhpQF7Q8ycsPp19LHTpVyGhYb0ustuRQ== + dependencies: + "@babel/runtime" "^7.0.0" + fbjs "^3.0.0" + +remote-redux-devtools@^0.5.12: + version "0.5.16" + resolved "https://registry.yarnpkg.com/remote-redux-devtools/-/remote-redux-devtools-0.5.16.tgz#95b1a4a1988147ca04f3368f3573b661748b3717" + integrity sha512-xZ2D1VRIWzat5nsvcraT6fKEX9Cfi+HbQBCwzNnUAM8Uicm/anOc60XGalcaDPrVmLug7nhDl2nimEa3bL3K9w== + dependencies: + jsan "^3.1.13" + querystring "^0.2.0" + redux-devtools-core "^0.2.1" + redux-devtools-instrument "^1.9.4" + rn-host-detect "^1.1.5" + socketcluster-client "^14.2.1" + +remotedev-serialize@^0.1.8: + version "0.1.9" + resolved "https://registry.yarnpkg.com/remotedev-serialize/-/remotedev-serialize-0.1.9.tgz#5e67e05cbca75d408d769d057dc59d0f56cd2c43" + integrity sha512-5tFdZg9mSaAWTv6xmQ7HtHjKMLSFQFExEZOtJe10PLsv1wb7cy7kYHtBvTYRro27/3fRGEcQBRNKSaixOpb69w== + dependencies: + jsan "^3.1.13" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +replace-ext@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= + +request@2.88.2, request@^2.55.0, request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +reselect-tree@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/reselect-tree/-/reselect-tree-1.3.4.tgz#449629728e2dc79bf0602571ec8859ac34737089" + integrity sha512-1OgNq1IStyJFqIqOoD3k3Ge4SsYCMP9W88VQOfvgyLniVKLfvbYO1Vrl92SyEK5021MkoBX6tWb381VxTDyPBQ== + dependencies: + debug "^3.1.0" + esdoc "^1.0.4" + json-pointer "^0.6.0" + reselect "^4.0.0" + source-map-support "^0.5.3" + +reselect@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" + integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== + +resolve-from@5.0.0, resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.10.0, resolve@^1.14.2: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +retry@0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^2.2.8, rimraf@^2.6.1, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.3: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== + dependencies: + bn.js "^4.11.1" + +rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +rn-host-detect@^1.1.5: + version "1.2.0" + resolved "https://registry.yarnpkg.com/rn-host-detect/-/rn-host-detect-1.2.0.tgz#8b0396fc05631ec60c1cb8789e5070cdb04d0da0" + integrity sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A== + +rsa-pem-to-jwk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/rsa-pem-to-jwk/-/rsa-pem-to-jwk-1.1.3.tgz#245e76bdb7e7234cfee7ca032d31b54c38fab98e" + integrity sha512-ZlVavEvTnD8Rzh/pdB8NH4VF5GNEtF6biGQcTtC4GKFMsbZR08oHtOYefbhCN+JnJIuMItiCDCMycdcMrw6blA== + dependencies: + object-assign "^2.0.0" + rsa-unpack "0.0.6" + +rsa-unpack@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/rsa-unpack/-/rsa-unpack-0.0.6.tgz#f50ebd56a628378e631f297161026ce9ab4eddba" + integrity sha512-HRrl8GHjjPziPFRDJPq/v5OxZ3IPdksV5h3cime/oHgcgM1k1toO5OdtzClgBqRf5dF6IgptOB0g/zFb0w5zQw== + dependencies: + optimist "~0.3.5" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.1.4, sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +sc-channel@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/sc-channel/-/sc-channel-1.2.0.tgz#d9209f3a91e3fa694c66b011ce55c4ad8c3087d9" + integrity sha512-M3gdq8PlKg0zWJSisWqAsMmTVxYRTpVRqw4CWAdKBgAfVKumFcTjoCV0hYu7lgUXccCtCD8Wk9VkkE+IXCxmZA== + dependencies: + component-emitter "1.2.1" + +sc-errors@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/sc-errors/-/sc-errors-2.0.1.tgz#3af2d934dfd82116279a4b2c1552c1e021ddcb03" + integrity sha512-JoVhq3Ud+3Ujv2SIG7W0XtjRHsrNgl6iXuHHsh0s+Kdt5NwI6N2EGAZD4iteitdDv68ENBkpjtSvN597/wxPSQ== + +sc-formatter@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/sc-formatter/-/sc-formatter-3.0.2.tgz#9abdb14e71873ce7157714d3002477bbdb33c4e6" + integrity sha512-9PbqYBpCq+OoEeRQ3QfFIGE6qwjjBcd2j7UjgDlhnZbtSnuGgHdcRklPKYGuYFH82V/dwd+AIpu8XvA1zqTd+A== + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^3.6.2: + version "3.8.0" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.0.tgz#28f59f4b01dbee9575f56a47034b7d2e3b3b352d" + integrity sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.5.2" + nan "^2.14.0" + safe-buffer "^5.1.2" + +secp256k1@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semaphore@>=1.0.1, semaphore@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.0.0: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + +semver@^7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +sentence-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" + integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ= + dependencies: + no-case "^2.2.0" + upper-case-first "^1.1.2" + +serialize-javascript@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallowequal@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signed-varint@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/signed-varint/-/signed-varint-2.0.1.tgz#50a9989da7c98c2c61dad119bc97470ef8528129" + integrity sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw== + dependencies: + varint "~5.0.0" + +signedsource@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" + integrity sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo= + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.2.tgz#5708fb0919d440657326cd5fe7d2599d07705019" + integrity sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + +socketcluster-client@^14.2.1: + version "14.3.1" + resolved "https://registry.yarnpkg.com/socketcluster-client/-/socketcluster-client-14.3.1.tgz#bfc3591c0cad2668e7b3512a102f3844f5f2e84d" + integrity sha512-Sd/T0K/9UlqTfz+HUuFq90dshA5OBJPQbdkRzGtcKIOm52fkdsBTt0FYpiuzzxv5VrU7PWpRm6KIfNXyPwlLpw== + dependencies: + buffer "^5.2.1" + clone "2.1.1" + component-emitter "1.2.1" + linked-list "0.1.0" + querystring "0.2.0" + sc-channel "^1.2.0" + sc-errors "^2.0.1" + sc-formatter "^3.0.1" + uuid "3.2.1" + ws "7.1.0" + +solc@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.2.tgz#6033d75c6166fd0feb7fe08eddc198aaf52025da" + integrity sha512-fMfcAPaePLfsOY82jqONt0RMh5M8m+pK6QtnMGMUFUm8uEDlUmoqnyLxGVFelosJaVfXhygAB+mTlb+HxiV7DQ== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +source-map-resolve@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + +source-map-support@^0.5.19, source-map-support@^0.5.3: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spark-md5@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.0.tgz#3722227c54e2faf24b1dc6d933cc144e6f71bfef" + integrity sha1-NyIifFTi+vJLHcbZM8wUTm9xv+8= + +spark-md5@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.1.tgz#83a0e255734f2ab4e5c466e5a2cfc9ba2aa2124d" + integrity sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + +split-ca@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== + +split2@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +sqlite3@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-4.2.0.tgz#49026d665e9fc4f922e56fb9711ba5b4c85c4901" + integrity sha512-roEOz41hxui2Q7uYnWsjMOTry6TcNUNmp8audCx18gF10P2NknwdpF+E+HKvz/F2NvPKGGBF4NGc+ZPQ+AABwg== + dependencies: + nan "^2.12.1" + node-pre-gyp "^0.11.0" + +sse-z@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/sse-z/-/sse-z-0.3.0.tgz#e215db7c303d6c4a4199d80cb63811cc28fa55b9" + integrity sha512-jfcXynl9oAOS9YJ7iqS2JMUEHOlvrRAD+54CENiWnc4xsuVLQVSgmwf7cwOTcBd/uq3XkQKBGojgvEtVXcJ/8w== + +sshpk@^1.7.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stable@~0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.5.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stoppable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" + integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +stream-to-pull-stream@^1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz#4161aa2d2eb9964de60bfa1af7feaf917e874ece" + integrity sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg== + dependencies: + looper "^3.0.0" + pull-stream "^3.2.3" + +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1, string_decoder@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" + integrity sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4= + dependencies: + first-chunk-stream "^1.0.0" + strip-bom "^2.0.0" + +strip-bom@2.X, strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== + dependencies: + is-hex-prefixed "1.0.0" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +strip-json-comments@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +sublevel-pouchdb@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/sublevel-pouchdb/-/sublevel-pouchdb-7.2.2.tgz#49e46cd37883bf7ff5006d7c5b9bcc7bcc1f422f" + integrity sha512-y5uYgwKDgXVyPZceTDGWsSFAhpSddY29l9PJbXqMJLfREdPmQTY8InpatohlEfCXX7s1LGcrfYAhxPFZaJOLnQ== + dependencies: + inherits "2.0.4" + level-codec "9.0.2" + ltgt "2.2.1" + readable-stream "1.1.14" + +subscriptions-transport-ws@^0.9.19: + version "0.9.19" + resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.19.tgz#10ca32f7e291d5ee8eb728b9c02e43c52606cdcf" + integrity sha512-dxdemxFFB0ppCLg10FTtRqH/31FNRL1y1BQv8209MK5I4CwALb7iihQg+7p65lFcIl8MHatINWBLOqpgU4Kyyw== + dependencies: + backo2 "^1.0.2" + eventemitter3 "^3.1.0" + iterall "^1.2.1" + symbol-observable "^1.0.4" + ws "^5.2.0 || ^6.0.0 || ^7.0.0" + +super-split@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/super-split/-/super-split-1.1.0.tgz#43b3ba719155f4d43891a32729d59b213d9155fc" + integrity sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ== + +supports-color@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swap-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" + integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= + dependencies: + lower-case "^1.1.1" + upper-case "^1.1.1" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +symbol-observable@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" + integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== + +"symbol-tree@>= 3.1.0 < 4.0.0": + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +symbol@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/symbol/-/symbol-0.2.3.tgz#3b9873b8a901e47c6efe21526a3ac372ef28bbc7" + integrity sha1-O5hzuKkB5Hxu/iFSajrDcu8ou8c= + +sync-fetch@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.3.0.tgz#77246da949389310ad978ab26790bb05f88d1335" + integrity sha512-dJp4qg+x4JwSEW1HibAuMi0IIrBI3wuQr2GimmqB7OXR50wmwzfdusG+p39R9w3R6aFtZ2mzvxvWKQ3Bd/vx3g== + dependencies: + buffer "^5.7.0" + node-fetch "^2.6.1" + +sync-request@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" + integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== + dependencies: + http-response-object "^3.0.1" + sync-rpc "^1.2.1" + then-request "^6.0.0" + +sync-rpc@^1.2.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" + integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== + dependencies: + get-port "^3.1.0" + +taffydb@2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.7.3.tgz#2ad37169629498fca5bc84243096d3cde0ec3a34" + integrity sha1-KtNxaWKUmPylvIQkMJbTzeDsOjQ= + +tar-fs@~1.16.3: + version "1.16.3" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" + integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.1" + pump "^1.0.0" + tar-stream "^1.1.2" + +tar-stream@^1.1.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tar-stream@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@^4, tar@^4.0.2: + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + +tar@^6.1.0: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +then-request@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" + integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== + dependencies: + "@types/concat-stream" "^1.6.0" + "@types/form-data" "0.0.33" + "@types/node" "^8.0.0" + "@types/qs" "^6.2.31" + caseless "~0.12.0" + concat-stream "^1.6.0" + form-data "^2.2.0" + http-basic "^8.1.1" + http-response-object "^3.0.1" + promise "^8.0.0" + qs "^6.4.0" + +through2-filter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" + integrity sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw= + dependencies: + through2 "~2.0.0" + xtend "~4.0.0" + +through2-filter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" + integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== + dependencies: + through2 "~2.0.0" + xtend "~4.0.0" + +through2@2.X, through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through2@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" + integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww== + dependencies: + readable-stream "2 || 3" + +through2@3.0.2, through2@^3.0.0, through2@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" + integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== + dependencies: + inherits "^2.0.4" + readable-stream "2 || 3" + +through2@^0.6.0: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tildify@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" + integrity sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo= + dependencies: + os-homedir "^1.0.0" + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +tiny-queue@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" + integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY= + +title-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" + integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o= + dependencies: + no-case "^2.2.0" + upper-case "^1.0.3" + +tmp-promise@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.2.tgz#6e933782abff8b00c3119d63589ca1fb9caaa62a" + integrity sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA== + dependencies: + tmp "^0.2.0" + +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +to-absolute-glob@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" + integrity sha1-HN+kcqnvUMI57maZm2YsoOs5k38= + dependencies: + extend-shallow "^2.0.1" + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tough-cookie@^2.2.0, tough-cookie@^2.3.1, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +"tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" + +tr46@~0.0.1, tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +truffle@^5.2: + version "5.2.3" + resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.2.3.tgz#6c1585da56b704397017833ea6b62e18303b924f" + integrity sha512-iOeOSaCZtQ+TWsEh6yc6Al+RVkWTsJQnceXNYSCYR86QcXssGY5CqDQ2JwIxwAN4YMRf4GZ/LRAPul6qX36b6A== + dependencies: + "@truffle/debugger" "^8.0.17" + app-module-path "^2.2.0" + mocha "8.1.2" + original-require "^1.0.1" + optionalDependencies: + "@truffle/db" "^0.5.3" + +ts-invariant@^0.4.0: + version "0.4.4" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86" + integrity sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA== + dependencies: + tslib "^1.9.3" + +ts-invariant@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.6.1.tgz#eb4c52b45daaca8367abbfd6cff998ea871d592d" + integrity sha512-QQgN33g8E8yrdDuH29HASveLtbzMnRRgWh0i/JNTW4+zcLsdIOnfsgEDi/NKx4UckQyuMFt9Ujm6TWLWQ58Kvg== + dependencies: + "@types/ungap__global-this" "^0.3.1" + "@ungap/global-this" "^0.4.2" + tslib "^1.9.3" + +tslib@^1.10.0, tslib@^1.14.1, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.3, tslib@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + +tslib@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + +tslib@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +tweetnacl@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-is@^1.6.16, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5, typedarray-to-buffer@~3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6, typedarray@~0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript-compare@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425" + integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA== + dependencies: + typescript-logic "^0.0.0" + +typescript-logic@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196" + integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q== + +typescript-tuple@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2" + integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q== + dependencies: + typescript-compare "^0.0.2" + +ua-parser-js@^0.7.18: + version "0.7.24" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" + integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw== + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f" + integrity sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.0" + has-symbols "^1.0.0" + which-boxed-primitive "^1.0.1" + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +underscore@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + +underscore@^1.8.3: + version "1.12.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.0.tgz#4814940551fc80587cef7840d1ebb0f16453be97" + integrity sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ== + +unique-by@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-by/-/unique-by-1.0.0.tgz#5220c86ba7bc572fb713ad74651470cb644212bd" + integrity sha512-rJRXK5V0zL6TiSzhoGNpJp5dr+TZBLoPJFC06rLn17Ug++7Aa0Qnve5v+skXeQxx6/sI7rBsSesa6MAcmFi8Ew== + +unique-stream@^2.0.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" + integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== + dependencies: + json-stable-stringify-without-jsonify "^1.0.1" + through2-filter "^3.0.0" + +unique-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" + integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= + dependencies: + crypto-random-string "^1.0.0" + +universalify@^0.1.0, universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" + integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unixify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" + integrity sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA= + dependencies: + normalize-path "^2.1.1" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +upper-case-first@^1.1.0, upper-case-first@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= + dependencies: + upper-case "^1.1.1" + +upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-parse@^1.4.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +ursa-optional@~0.10.0: + version "0.10.2" + resolved "https://registry.yarnpkg.com/ursa-optional/-/ursa-optional-0.10.2.tgz#bd74e7d60289c22ac2a69a3c8dea5eb2817f9681" + integrity sha512-TKdwuLboBn7M34RcvVTuQyhvrA8gYKapuVdm0nBP0mnBc7oECOfUQZrY91cefL3/nm64ZyrejSRrhTVdX7NG/A== + dependencies: + bindings "^1.5.0" + nan "^2.14.2" + +utf-8-validate@^5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.4.tgz#72a1735983ddf7a05a43a9c6b67c5ce1c910f9b8" + integrity sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q== + dependencies: + node-gyp-build "^4.2.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util.promisify@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" + integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + for-each "^0.3.3" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.1" + +util@^0.12.0: + version "0.12.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" + integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== + +uuid@^3.1.0, uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.0.0, uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +vali-date@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6" + integrity sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY= + +valid-url@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +varint@^5.0.0, varint@~5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vinyl-fs@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-2.4.3.tgz#3d97e562ebfdd4b66921dea70626b84bde9d2d07" + integrity sha1-PZflYuv91LZpId6nBia4S96dLQc= + dependencies: + duplexify "^3.2.0" + glob-stream "^5.3.2" + graceful-fs "^4.0.0" + gulp-sourcemaps "^1.5.2" + is-valid-glob "^0.3.0" + lazystream "^1.0.0" + lodash.isequal "^4.0.0" + merge-stream "^1.0.0" + mkdirp "^0.5.0" + object-assign "^4.0.0" + readable-stream "^2.0.4" + strip-bom "^2.0.0" + strip-bom-stream "^1.0.0" + through2 "^2.0.0" + through2-filter "^2.0.0" + vali-date "^1.0.0" + vinyl "^1.0.0" + +vinyl@1.X, vinyl@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" + integrity sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ= + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +vuvuzela@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/vuvuzela/-/vuvuzela-1.0.3.tgz#3be145e58271c73ca55279dd851f12a682114b0b" + integrity sha1-O+FF5YJxxzylUnndhR8SpoIRSws= + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +web3-bzz@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.9.tgz#25f8a373bc2dd019f47bf80523546f98b93c8790" + integrity sha512-ogVQr9jHodu9HobARtvUSmWG22cv2EUQzlPeejGWZ7j5h20HX40EDuWyomGY5VclIj5DdLY76Tmq88RTf/6nxA== + dependencies: + "@types/node" "^10.12.18" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.9.1" + +web3-bzz@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.3.4.tgz#9be529353c4063bc68395370cb5d8e414c6b6c87" + integrity sha512-DBRVQB8FAgoAtZCpp2GAGPCJjgBgsuwOKEasjV044AAZiONpXcKHbkO6G1SgItIixnrJsRJpoGLGw52Byr6FKw== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.9.1" + +web3-core-helpers@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.9.tgz#6381077c3e01c127018cb9e9e3d1422697123315" + integrity sha512-t0WAG3orLCE3lqi77ZoSRNFok3VQWZXTniZigDQjyOJYMAX7BU3F3js8HKbjVnAxlX3tiKoDxI0KBk9F3AxYuw== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.9" + web3-utils "1.2.9" + +web3-core-helpers@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.3.4.tgz#b8549740bf24d5c71688d89c3cdd802d8d36b4e4" + integrity sha512-n7BqDalcTa1stncHMmrnFtyTgDhX5Fy+avNaHCf6qcOP2lwTQC8+mdHVBONWRJ6Yddvln+c8oY/TAaB6PzWK0A== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.3.4" + web3-utils "1.3.4" + +web3-core-method@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.9.tgz#3fb538751029bea570e4f86731e2fa5e4945e462" + integrity sha512-bjsIoqP3gs7A/gP8+QeLUCyOKJ8bopteCSNbCX36Pxk6TYfYWNuC6hP+2GzUuqdP3xaZNe+XEElQFUNpR3oyAg== + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.9.1" + web3-core-helpers "1.2.9" + web3-core-promievent "1.2.9" + web3-core-subscriptions "1.2.9" + web3-utils "1.2.9" + +web3-core-method@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.3.4.tgz#6c2812d96dd6c811b9e6c8a5d25050d2c22b9527" + integrity sha512-JxmQrujsAWYRRN77P/RY7XuZDCzxSiiQJrgX/60Lfyf7FF1Y0le4L/UMCi7vUJnuYkbU1Kfl9E0udnqwyPqlvQ== + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.9.1" + web3-core-helpers "1.3.4" + web3-core-promievent "1.3.4" + web3-core-subscriptions "1.3.4" + web3-utils "1.3.4" + +web3-core-promievent@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.9.tgz#bb1c56aa6fac2f4b3c598510f06554d25c11c553" + integrity sha512-0eAUA2zjgXTleSrnc1wdoKQPPIHU6KHf4fAscu4W9kKrR+mqP1KsjYrxY9wUyjNnXxfQ+5M29ipvbiaK8OqdOw== + dependencies: + eventemitter3 "3.1.2" + +web3-core-promievent@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.3.4.tgz#d166239012d91496cdcbe91d5d54071ea818bc73" + integrity sha512-V61dZIeBwogg6hhZZUt0qL9hTp1WDhnsdjP++9fhTDr4vy/Gz8T5vibqT2LLg6lQC8i+Py33yOpMeMNjztaUaw== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.9.tgz#dd6d855256c4dd681434fe0867f8cd742fe10503" + integrity sha512-1PwKV2m46ALUnIN5VPPgjOj8yMLJhhqZYvYJE34hTN5SErOkwhzx5zScvo5MN7v7KyQGFnpVCZKKGCiEnDmtFA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.9" + web3-providers-http "1.2.9" + web3-providers-ipc "1.2.9" + web3-providers-ws "1.2.9" + +web3-core-requestmanager@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.3.4.tgz#e105ced735c2b5fcedd5771e0ecf9879ae9c373f" + integrity sha512-xriouCrhVnVDYQ04TZXdEREZm0OOJzkSEsoN5bu4JYsA6e/HzROeU+RjDpMUxFMzN4wxmFZ+HWbpPndS3QwMag== + dependencies: + underscore "1.9.1" + util "^0.12.0" + web3-core-helpers "1.3.4" + web3-providers-http "1.3.4" + web3-providers-ipc "1.3.4" + web3-providers-ws "1.3.4" + +web3-core-subscriptions@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.9.tgz#335fd7d15dfce5d78b4b7bef05ce4b3d7237b0e4" + integrity sha512-Y48TvXPSPxEM33OmXjGVDMzTd0j8X0t2+sDw66haeBS8eYnrEzasWuBZZXDq0zNUsqyxItgBGDn+cszkgEnFqg== + dependencies: + eventemitter3 "3.1.2" + underscore "1.9.1" + web3-core-helpers "1.2.9" + +web3-core-subscriptions@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.3.4.tgz#7b00e92bde21f792620cd02e6e508fcf4f4c31d3" + integrity sha512-drVHVDxh54hv7xmjIm44g4IXjfGj022fGw4/meB5R2D8UATFI40F73CdiBlyqk3DysP9njDOLTJFSQvEkLFUOg== + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.3.4" + +web3-core@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.9.tgz#2cba57aa259b6409db532d21bdf57db8d504fd3e" + integrity sha512-fSYv21IP658Ty2wAuU9iqmW7V+75DOYMVZsDH/c14jcF/1VXnedOcxzxSj3vArsCvXZNe6XC5/wAuGZyQwR9RA== + dependencies: + "@types/bn.js" "^4.11.4" + "@types/node" "^12.6.1" + bignumber.js "^9.0.0" + web3-core-helpers "1.2.9" + web3-core-method "1.2.9" + web3-core-requestmanager "1.2.9" + web3-utils "1.2.9" + +web3-core@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.3.4.tgz#2cc7ba7f35cc167f7a0a46fd5855f86e51d34ce8" + integrity sha512-7OJu46RpCEfTerl+gPvHXANR2RkLqAfW7l2DAvQ7wN0pnCzl9nEfdgW6tMhr31k3TR2fWucwKzCyyxMGzMHeSA== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-core-requestmanager "1.3.4" + web3-utils "1.3.4" + +web3-eth-abi@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.9.tgz#14bedd7e4be04fcca35b2ac84af1400574cd8280" + integrity sha512-3YwUYbh/DMfDbhMWEebAdjSd5bj3ZQieOjLzWFHU23CaLEqT34sUix1lba+hgUH/EN6A7bKAuKOhR3p0OvTn7Q== + dependencies: + "@ethersproject/abi" "5.0.0-beta.153" + underscore "1.9.1" + web3-utils "1.2.9" + +web3-eth-abi@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.3.4.tgz#10f5d8b6080dbb6cbaa1bcef7e0c70573da6566f" + integrity sha512-PVSLXJ2dzdXsC+R24llIIEOS6S1KhG5qwNznJjJvXZFe3sqgdSe47eNvwUamZtCBjcrdR/HQr+L/FTxqJSf80Q== + dependencies: + "@ethersproject/abi" "5.0.7" + underscore "1.9.1" + web3-utils "1.3.4" + +web3-eth-abi@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.7.0.tgz#4fac9c7d9e5a62b57f8884b37371f515c766f3f4" + integrity sha512-heqR0bWxgCJwjWIhq2sGyNj9bwun5+Xox/LdZKe+WMyTSy0cXDXEAgv3XKNkXC4JqdDt/ZlbTEx4TWak4TRMSg== + dependencies: + "@ethersproject/abi" "5.0.7" + web3-utils "1.7.0" + +web3-eth-accounts@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.9.tgz#7ec422df90fecb5243603ea49dc28726db7bdab6" + integrity sha512-jkbDCZoA1qv53mFcRHCinoCsgg8WH+M0YUO1awxmqWXRmCRws1wW0TsuSQ14UThih5Dxolgl+e+aGWxG58LMwg== + dependencies: + crypto-browserify "3.12.0" + eth-lib "^0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.9" + web3-core-helpers "1.2.9" + web3-core-method "1.2.9" + web3-utils "1.2.9" + +web3-eth-accounts@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.3.4.tgz#cf513d78531c13ce079a5e7862820570350e79a5" + integrity sha512-gz9ReSmQEjqbYAjpmAx+UZF4CVMbyS4pfjSYWGAnNNI+Xz0f0u0kCIYXQ1UEaE+YeLcYiE+ZlZdgg6YoatO5nA== + dependencies: + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-utils "1.3.4" + +web3-eth-contract@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.9.tgz#713d9c6d502d8c8f22b696b7ffd8e254444e6bfd" + integrity sha512-PYMvJf7EG/HyssUZa+pXrc8IB06K/YFfWYyW4R7ed3sab+9wWUys1TlWxBCBuiBXOokSAyM6H6P6/cKEx8FT8Q== + dependencies: + "@types/bn.js" "^4.11.4" + underscore "1.9.1" + web3-core "1.2.9" + web3-core-helpers "1.2.9" + web3-core-method "1.2.9" + web3-core-promievent "1.2.9" + web3-core-subscriptions "1.2.9" + web3-eth-abi "1.2.9" + web3-utils "1.2.9" + +web3-eth-contract@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.3.4.tgz#1ea2dd71be0c4a9cf4772d4f75dbb2fa99751472" + integrity sha512-Fvy8ZxUksQY2ePt+XynFfOiSqxgQtMn4m2NJs6VXRl2Inl17qyRi/nIJJVKTcENLocm+GmZ/mxq2eOE5u02nPg== + dependencies: + "@types/bn.js" "^4.11.5" + underscore "1.9.1" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-core-promievent "1.3.4" + web3-core-subscriptions "1.3.4" + web3-eth-abi "1.3.4" + web3-utils "1.3.4" + +web3-eth-ens@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.9.tgz#577b9358c036337833fb2bdc59c11be7f6f731b6" + integrity sha512-kG4+ZRgZ8I1WYyOBGI8QVRHfUSbbJjvJAGA1AF/NOW7JXQ+x7gBGeJw6taDWJhSshMoEKWcsgvsiuoG4870YxQ== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.9" + web3-core-helpers "1.2.9" + web3-core-promievent "1.2.9" + web3-eth-abi "1.2.9" + web3-eth-contract "1.2.9" + web3-utils "1.2.9" + +web3-eth-ens@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.3.4.tgz#a7e4bb18481fb0e2ce5bfb3b3da2fbb0ad78cefe" + integrity sha512-b0580tQyQwpV2wyacwQiBEfQmjCUln5iPhge3IBIMXaI43BUNtH3lsCL9ERFQeOdweB4o+6rYyNYr6xbRcSytg== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-promievent "1.3.4" + web3-eth-abi "1.3.4" + web3-eth-contract "1.3.4" + web3-utils "1.3.4" + +web3-eth-iban@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.9.tgz#4ebf3d8783f34d04c4740dc18938556466399f7a" + integrity sha512-RtdVvJE0pyg9dHLy0GzDiqgnLnssSzfz/JYguhC1wsj9+Gnq1M6Diy3NixACWUAp6ty/zafyOaZnNQ+JuH9TjQ== + dependencies: + bn.js "4.11.8" + web3-utils "1.2.9" + +web3-eth-iban@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.3.4.tgz#5eb7a564e0dcf68730d68f48f95dd207cd173d81" + integrity sha512-Y7/hLjVvIN/OhaAyZ8L/hxbTqVX6AFTl2RwUXR6EEU9oaLydPcMjAx/Fr8mghUvQS3QJSr+UGubP3W4SkyNiYw== + dependencies: + bn.js "^4.11.9" + web3-utils "1.3.4" + +web3-eth-personal@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.9.tgz#9b95eb159b950b83cd8ae15873e1d57711b7a368" + integrity sha512-cFiNrktxZ1C/rIdJFzQTvFn3/0zcsR3a+Jf8Y3KxeQDHszQtosjLWptP7bsUmDwEh4hzh0Cy3KpOxlYBWB8bJQ== + dependencies: + "@types/node" "^12.6.1" + web3-core "1.2.9" + web3-core-helpers "1.2.9" + web3-core-method "1.2.9" + web3-net "1.2.9" + web3-utils "1.2.9" + +web3-eth-personal@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.3.4.tgz#0d0e0abea3447283d7ee5658ed312990c9bf48dd" + integrity sha512-JiTbaktYVk1j+S2EDooXAhw5j/VsdvZfKRmHtXUe/HizPM9ETXmj1+ne4RT6m+950jQ7DJwUF3XU1FKYNtEDwQ== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-net "1.3.4" + web3-utils "1.3.4" + +web3-eth@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.9.tgz#e40e7b88baffc9b487193211c8b424dc944977b3" + integrity sha512-sIKO4iE9FEBa/CYUd6GdPd7GXt/wISqxUd8PlIld6+hvMJj02lgO7Z7p5T9mZIJcIZJGvZX81ogx8oJ9yif+Ag== + dependencies: + underscore "1.9.1" + web3-core "1.2.9" + web3-core-helpers "1.2.9" + web3-core-method "1.2.9" + web3-core-subscriptions "1.2.9" + web3-eth-abi "1.2.9" + web3-eth-accounts "1.2.9" + web3-eth-contract "1.2.9" + web3-eth-ens "1.2.9" + web3-eth-iban "1.2.9" + web3-eth-personal "1.2.9" + web3-net "1.2.9" + web3-utils "1.2.9" + +web3-eth@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.3.4.tgz#7c4607685e66a1c43e3e315e526c959f24f96907" + integrity sha512-8OIVMLbvmx+LB5RZ4tDhXuFGWSdNMrCZ4HM0+PywQ08uEcmAcqTMFAn4vdPii+J8gCatZR501r1KdzX3SDLoPw== + dependencies: + underscore "1.9.1" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-core-subscriptions "1.3.4" + web3-eth-abi "1.3.4" + web3-eth-accounts "1.3.4" + web3-eth-contract "1.3.4" + web3-eth-ens "1.3.4" + web3-eth-iban "1.3.4" + web3-eth-personal "1.3.4" + web3-net "1.3.4" + web3-utils "1.3.4" + +web3-net@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.9.tgz#51d248ed1bc5c37713c4ac40c0073d9beacd87d3" + integrity sha512-d2mTn8jPlg+SI2hTj2b32Qan6DmtU9ap/IUlJTeQbZQSkTLf0u9suW8Vjwyr4poJYXTurdSshE7OZsPNn30/ZA== + dependencies: + web3-core "1.2.9" + web3-core-method "1.2.9" + web3-utils "1.2.9" + +web3-net@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.3.4.tgz#d76158bf0b4a7b3b14352b4f95472db9efc57a2a" + integrity sha512-wVyqgVC3Zt/0uGnBiR3GpnsS8lvOFTDgWZMxAk9C6Guh8aJD9MUc7pbsw5rHrPUVe6S6RUfFJvh/Xq8oMIQgSw== + dependencies: + web3-core "1.3.4" + web3-core-method "1.3.4" + web3-utils "1.3.4" + +web3-providers-http@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.9.tgz#e698aa5377e2019c24c5a1e6efa0f51018728934" + integrity sha512-F956tCIj60Ttr0UvEHWFIhx+be3He8msoPzyA44/kfzzYoMAsCFRn5cf0zQG6al0znE75g6HlWVSN6s3yAh51A== + dependencies: + web3-core-helpers "1.2.9" + xhr2-cookies "1.1.0" + +web3-providers-http@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.3.4.tgz#89389e18e27148faa2fef58842740ffadbdda8cc" + integrity sha512-aIg/xHXvxpqpFU70sqfp+JC3sGkLfAimRKTUhG4oJZ7U+tTcYTHoxBJj+4A3Id4JAoKiiv0k1/qeyQ8f3rMC3g== + dependencies: + web3-core-helpers "1.3.4" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.9.tgz#6159eacfcd7ac31edc470d93ef10814fe874763b" + integrity sha512-NQ8QnBleoHA2qTJlqoWu7EJAD/FR5uimf7Ielzk4Z2z+m+6UAuJdJMSuQNj+Umhz9L/Ys6vpS1vHx9NizFl+aQ== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.9" + +web3-providers-ipc@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.3.4.tgz#b963518989b1b7847063cdd461ff73b83855834a" + integrity sha512-E0CvXEJElr/TIlG1YfJeO3Le5NI/4JZM+1SsEdiPIfBUAJN18oOoum138EBGKv5+YaLKZUtUuJSXWjIIOR/0Ig== + dependencies: + oboe "2.1.5" + underscore "1.9.1" + web3-core-helpers "1.3.4" + +web3-providers-ws@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.9.tgz#22c2006655ec44b4ad2b41acae62741a6ae7a88c" + integrity sha512-6+UpvINeI//dglZoAKStUXqxDOXJy6Iitv2z3dbgInG4zb8tkYl/VBDL80UjUg3ZvzWG0g7EKY2nRPEpON2TFA== + dependencies: + eventemitter3 "^4.0.0" + underscore "1.9.1" + web3-core-helpers "1.2.9" + websocket "^1.0.31" + +web3-providers-ws@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.3.4.tgz#b94c2e0ec51a0c472abdec53a472b5bf8176bec1" + integrity sha512-WBd9hk2fUAdrbA3kUyUk94ZeILtE6txLeoVVvIKAw2bPegx+RjkLyxC1Du0oceKgQ/qQWod8CCzl1E/GgTP+MQ== + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.3.4" + websocket "^1.0.32" + +web3-shh@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.9.tgz#c4ba70d6142cfd61341a50752d8cace9a0370911" + integrity sha512-PWa8b/EaxaMinFaxy6cV0i0EOi2M7a/ST+9k9nhyhCjVa2vzXuNoBNo2IUOmeZ0WP2UQB8ByJ2+p4htlJaDOjA== + dependencies: + web3-core "1.2.9" + web3-core-method "1.2.9" + web3-core-subscriptions "1.2.9" + web3-net "1.2.9" + +web3-shh@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.3.4.tgz#b7d29e118f26416c1a74575e585be379cc01a77a" + integrity sha512-zoeww5mxLh3xKcqbX85irQbtFe5pc5XwrgjvmdMkhkOdZzPASlWOgqzUFtaPykpLwC3yavVx4jG5RqifweXLUA== + dependencies: + web3-core "1.3.4" + web3-core-method "1.3.4" + web3-core-subscriptions "1.3.4" + web3-net "1.3.4" + +web3-utils@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.9.tgz#abe11735221627da943971ef1a630868fb9c61f3" + integrity sha512-9hcpuis3n/LxFzEVjwnVgvJzTirS2S9/MiNAa7l4WOEoywY+BSNwnRX4MuHnjkh9NY25B6QOjuNG6FNnSjTw1w== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.4.tgz#9b1aa30d7549f860b573e7bb7e690999e7192198" + integrity sha512-/vC2v0MaZNpWooJfpRw63u0Y3ag2gNjAWiLtMSL6QQLmCqCy4SQIndMt/vRyx0uMoeGt1YTwSXEcHjUzOhLg0A== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.0.tgz#c59f0fd43b2449357296eb54541810b99b1c771c" + integrity sha512-O8Tl4Ky40Sp6pe89Olk2FsaUkgHyb5QAXuaKo38ms3CxZZ4d3rPGfjP9DNKGm5+IUgAZBNpF1VmlSmNCqfDI1w== + dependencies: + bn.js "^4.11.9" + ethereum-bloom-filters "^1.0.6" + ethereumjs-util "^7.1.0" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3@1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.9.tgz#cbcf1c0fba5e213a6dfb1f2c1f4b37062e4ce337" + integrity sha512-Mo5aBRm0JrcNpN/g4VOrDzudymfOnHRC3s2VarhYxRA8aWgF5rnhQ0ziySaugpic1gksbXPe105pUWyRqw8HUA== + dependencies: + web3-bzz "1.2.9" + web3-core "1.2.9" + web3-eth "1.2.9" + web3-eth-personal "1.2.9" + web3-net "1.2.9" + web3-shh "1.2.9" + web3-utils "1.2.9" + +web3@^1.0.0-beta.34: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.3.4.tgz#31e014873360aa5840eb17f9f171190c967cffb7" + integrity sha512-D6cMb2EtTMLHgdGbkTPGl/Qi7DAfczR+Lp7iFX3bcu/bsD9V8fZW69hA8v5cRPNGzXUwVQebk3bS17WKR4cD2w== + dependencies: + web3-bzz "1.3.4" + web3-core "1.3.4" + web3-eth "1.3.4" + web3-eth-personal "1.3.4" + web3-net "1.3.4" + web3-shh "1.3.4" + web3-utils "1.3.4" + +webidl-conversions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" + integrity sha1-O/glj30xjHRDw28uFpQCoaZwNQY= + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +websocket@^1.0.31, websocket@^1.0.32: + version "1.0.33" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.33.tgz#407f763fc58e74a3fa41ca3ae5d78d3f5e3b82a5" + integrity sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +websql@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/websql/-/websql-1.0.0.tgz#1bd00b27392893134715d5dd6941fd89e730bab5" + integrity sha512-7iZ+u28Ljw5hCnMiq0BCOeSYf0vCFQe/ORY0HgscTiKjQed8WqugpBUggJ2NTnB9fahn1kEnPRX2jf8Px5PhJw== + dependencies: + argsarray "^0.0.1" + immediate "^3.2.2" + noop-fn "^1.0.0" + sqlite3 "^4.0.0" + tiny-queue "^0.2.1" + +whatwg-fetch@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" + integrity sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ= + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +whatwg-url-compat@~0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" + integrity sha1-AImBEa9om7CXVBzVpFymyHmERb8= + dependencies: + tr46 "~0.0.1" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +which@2.0.2, which@^2.0.0, which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3, wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw== + +workerpool@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" + integrity sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA== + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^2.0.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-stream@~0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/write-stream/-/write-stream-0.4.3.tgz#83cc8c0347d0af6057a93862b4e3ae01de5c81c1" + integrity sha1-g8yMA0fQr2BXqThitOOuAd5cgcE= + dependencies: + readable-stream "~0.0.2" + +ws@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.0.tgz#0395646c6fcc3ac56abf61ce1a42039637a6bd98" + integrity sha512-Swie2C4fs7CkwlHu1glMePLYJJsWjzhl1vm3ZaLplD0h7OMkZyZ6kLTB/OagiU923bZrPFXuDTeEqaEN4NWG4g== + dependencies: + async-limiter "^1.0.0" + +ws@7.4.3: + version "7.4.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" + integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +"ws@^5.2.0 || ^6.0.0 || ^7.0.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" + integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== + +ws@^7.4.5: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +xdg-basedir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" + integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +"xml-name-validator@>= 2.0.1 < 3.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + integrity sha1-TYuPHszTQZqjYgYb7O9RXh5VljU= + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xss@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.10.tgz#5cd63a9b147a755a14cb0455c7db8866120eb4d2" + integrity sha512-qmoqrRksmzqSKvgqzN0055UFWY7OKx1/9JWeRswwEVX9fCG5jcYRxa/A2DHcmZX6VJvjzHRQ2STeeVcQkrmLSw== + dependencies: + commander "^2.20.3" + cssfilter "0.0.10" + +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +y18n@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" + integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.9.2.tgz#f0cfa865f003ab707663e4f04b3956957ea564ed" + integrity sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg== + dependencies: + "@babel/runtime" "^7.9.2" + +yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^15.0.1: + version "15.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" + integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^16.1.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1" + integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.0.6" + +yargs-unparser@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.1.tgz#bd4b0ee05b4c94d058929c32cb09e3fce71d3c5f" + integrity sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA== + dependencies: + camelcase "^5.3.1" + decamelize "^1.2.0" + flat "^4.1.0" + is-plain-obj "^1.1.0" + yargs "^14.2.3" + +yargs@13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.6.0.tgz#cb4050c0159bfb6bb649c0f4af550526a84619dc" + integrity sha1-y0BQwBWb+2u2ScD0r1UFJqhGGdw= + dependencies: + camelcase "^2.0.1" + cliui "^3.2.0" + decamelize "^1.1.1" + lodash.assign "^4.0.3" + os-locale "^1.4.0" + pkg-conf "^1.1.2" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + string-width "^1.0.1" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^2.4.0" + +yargs@^14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^15.0.1" + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zen-observable-ts@^0.8.21: + version "0.8.21" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d" + integrity sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg== + dependencies: + tslib "^1.9.3" + zen-observable "^0.8.0" + +zen-observable@^0.8.0, zen-observable@^0.8.14: + version "0.8.15" + resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== diff --git a/tests/src/docker_utils.rs b/tests/src/docker_utils.rs new file mode 100644 index 00000000000..617dbf11c3e --- /dev/null +++ b/tests/src/docker_utils.rs @@ -0,0 +1,269 @@ +use bollard::image::CreateImageOptions; +use bollard::models::HostConfig; +use bollard::{container, Docker}; +use futures::stream::FuturesUnordered; +use futures::StreamExt; +use std::collections::HashMap; +use tokio::time::{sleep, Duration}; + +use crate::helpers::{contains_subslice, MappedPorts}; + +type DockerError = bollard::errors::Error; + +const POSTGRES_IMAGE: &str = "postgres:latest"; +const IPFS_IMAGE: &str = "ipfs/go-ipfs:v0.10.0"; +const GANACHE_IMAGE: &str = "trufflesuite/ganache-cli:latest"; + +pub async fn pull_service_images() { + let client = + Docker::connect_with_local_defaults().expect("Failed to connect to docker daemon."); + + let image_names = [POSTGRES_IMAGE, IPFS_IMAGE, GANACHE_IMAGE]; + + image_names + .iter() + .map(|image_name| pull_image(&client, image_name)) + // .await them in no specific order. + .collect::>() + .collect::>() + .await; +} + +async fn pull_image(client: &Docker, image_name: &str) { + let options = Some(CreateImageOptions { + from_image: image_name, + ..Default::default() + }); + + let mut stream = client.create_image(options, None, None); + + while let Some(res) = stream.next().await { + if let Err(err) = res { + panic!("Error when pulling docker image `{}`: {}", image_name, err) + } + } +} + +pub async fn kill_and_remove(client: &Docker, container_name: &str) -> Result<(), DockerError> { + client.kill_container::<&str>(container_name, None).await?; + client.remove_container(container_name, None).await +} + +/// Represents all possible service containers to be spawned +#[derive(Debug, Copy, Clone)] +pub enum ServiceContainerKind { + Postgres, + Ipfs, + Ganache, +} + +impl ServiceContainerKind { + fn config(&self) -> container::Config<&'static str> { + let host_config = HostConfig { + publish_all_ports: Some(true), + ..Default::default() + }; + + match self { + Self::Postgres => container::Config { + image: Some(POSTGRES_IMAGE), + env: Some(vec![ + "POSTGRES_PASSWORD=password", + "POSTGRES_USER=postgres", + "POSTGRES_INITDB_ARGS=-E UTF8 --locale=C", + ]), + host_config: Some(host_config), + cmd: Some(vec![ + "postgres", + "-N", + "1000", + "-cshared_preload_libraries=pg_stat_statements", + ]), + ..Default::default() + }, + + Self::Ipfs => container::Config { + image: Some(IPFS_IMAGE), + host_config: Some(host_config), + ..Default::default() + }, + + Self::Ganache => container::Config { + image: Some(GANACHE_IMAGE), + cmd: Some(vec!["-d", "-l", "100000000000", "-g", "1"]), + host_config: Some(host_config), + ..Default::default() + }, + } + } + + pub fn name(&self) -> &str { + use ServiceContainerKind::*; + match self { + Postgres => "graph_node_integration_test_postgres", + Ipfs => "graph_node_integration_test_ipfs", + Ganache => "graph_node_integration_test_ganache", + } + } +} + +/// Handles the connection to the docker daemon and keeps track the service running inside it. +pub struct ServiceContainer { + client: Docker, + container_name: String, +} + +impl ServiceContainer { + pub async fn start(service: ServiceContainerKind) -> Result { + let client = + Docker::connect_with_local_defaults().expect("Failed to connect to docker daemon."); + let container_name = + format!("{}-{}", service.name(), uuid::Uuid::new_v4()).replace("-", "_"); + + let docker_test_client = Self { + container_name: container_name.clone(), + client, + }; + + docker_test_client + .client + .create_container( + Some(container::CreateContainerOptions { + name: container_name.clone(), + }), + service.config(), + ) + .await?; + + docker_test_client + .client + .start_container::<&'static str>(&container_name, None) + .await?; + + Ok(docker_test_client) + } + + pub fn container_name(&self) -> &str { + &self.container_name + } + + pub async fn stop(&self) -> Result<(), DockerError> { + kill_and_remove(&self.client, self.container_name()).await + } + + pub async fn exposed_ports(&self) -> Result { + use bollard::models::ContainerSummaryInner; + + let results = { + let mut filters = HashMap::new(); + filters.insert("name".to_string(), vec![self.container_name().to_string()]); + let options = Some(container::ListContainersOptions { + filters, + limit: Some(1), + ..Default::default() + }); + self.client.list_containers(options).await? + }; + + let ports = match &results.as_slice() { + &[ContainerSummaryInner { + ports: Some(ports), .. + }] => ports, + unexpected_response => panic!( + "Received a unexpected_response from docker API: {:#?}", + unexpected_response + ), + }; + + Ok(to_mapped_ports(ports.to_vec())) + } + + /// halts execution until a trigger message is detected on stdout or, optionally, + /// waits for a specified amount of time after the message appears. + pub async fn wait_for_message( + &self, + trigger_message: &[u8], + hard_wait: Duration, + ) -> Result<&Self, DockerError> { + // listen to container logs + let mut stream = self.client.logs::( + &self.container_name, + Some(container::LogsOptions { + follow: true, + stdout: true, + stderr: true, + ..Default::default() + }), + ); + + // halt execution until a message is received + loop { + match stream.next().await { + Some(Ok(log_line)) => { + if contains_subslice(log_line.to_string().as_bytes(), trigger_message) { + break; + } + } + Some(Err(error)) => return Err(error), + None => panic!("stream ended before expected message could be detected"), + } + } + + sleep(hard_wait).await; + Ok(self) + } + + /// Calls `docker exec` on the container to create a test database. + pub async fn create_postgres_database( + docker: &ServiceContainer, + db_name: &str, + ) -> Result<(), DockerError> { + use bollard::exec; + + // 1. Create Exec + let config = exec::CreateExecOptions { + cmd: Some(vec!["createdb", "-E", "UTF8", "--locale=C", &db_name]), + user: Some("postgres"), + attach_stdout: Some(true), + ..Default::default() + }; + + let message = docker + .client + .create_exec(docker.container_name(), config) + .await?; + + // 2. Start Exec + let mut stream = docker.client.start_exec(&message.id, None); + while let Some(_) = stream.next().await { /* consume stream */ } + + // 3. Inspect exec + let inspect = docker.client.inspect_exec(&message.id).await?; + match inspect.exit_code { + Some(0) => Ok(()), + code => panic!( + "failed to run 'createdb' command using docker exec (exit code: {:?})", + code + ), + } + } +} + +fn to_mapped_ports(input: Vec) -> MappedPorts { + let mut hashmap = HashMap::new(); + + for port in &input { + if let bollard::models::Port { + private_port, + public_port: Some(public_port), + .. + } = port + { + hashmap.insert(*private_port as u16, *public_port as u16); + } + } + if hashmap.is_empty() { + panic!("Container exposed no ports. Input={:?}", input) + } + hashmap +} diff --git a/tests/src/fixture.rs b/tests/src/fixture/mod.rs similarity index 93% rename from tests/src/fixture.rs rename to tests/src/fixture/mod.rs index 5a489bbdf72..52405453952 100644 --- a/tests/src/fixture.rs +++ b/tests/src/fixture/mod.rs @@ -1,11 +1,9 @@ pub mod ethereum; use std::marker::PhantomData; -use std::process::Command; use std::sync::Mutex; use std::time::Duration; -use crate::helpers::run_cmd; use anyhow::Error; use async_stream::stream; use futures::{Stream, StreamExt}; @@ -53,51 +51,6 @@ use tokio::fs::read_to_string; const NODE_ID: &str = "default"; -pub async fn build_subgraph(dir: &str) -> DeploymentHash { - build_subgraph_with_yarn_cmd(dir, "deploy:test").await -} - -pub async fn build_subgraph_with_yarn_cmd(dir: &str, yarn_cmd: &str) -> DeploymentHash { - // Test that IPFS is up. - IpfsClient::localhost() - .test() - .await - .expect("Could not connect to IPFS, make sure it's running at port 5001"); - - // Make sure dependencies are present. - run_cmd( - Command::new("yarn") - .arg("install") - .arg("--mutex") - .arg("file:.yarn-mutex") - .current_dir("./integration-tests"), - ); - - // Run codegen. - run_cmd(Command::new("yarn").arg("codegen").current_dir(&dir)); - - // Run `deploy` for the side effect of uploading to IPFS, the graph node url - // is fake and the actual deploy call is meant to fail. - let deploy_output = run_cmd( - Command::new("yarn") - .arg(yarn_cmd) - .env("IPFS_URI", "http://127.0.0.1:5001") - .env("GRAPH_NODE_ADMIN_URI", "http://localhost:0") - .current_dir(dir), - ); - - // Hack to extract deployment id from `graph deploy` output. - const ID_PREFIX: &str = "Build completed: "; - let mut line = deploy_output - .lines() - .find(|line| line.contains(ID_PREFIX)) - .expect("found no matching line"); - if !line.starts_with(ID_PREFIX) { - line = &line[5..line.len() - 5]; // workaround for colored output - } - DeploymentHash::new(line.trim_start_matches(ID_PREFIX)).unwrap() -} - pub fn test_ptr(n: BlockNumber) -> BlockPtr { test_ptr_reorged(n, 0) } diff --git a/tests/src/helpers.rs b/tests/src/helpers.rs index 02597b183d4..21d520c00d2 100644 --- a/tests/src/helpers.rs +++ b/tests/src/helpers.rs @@ -1,26 +1,18 @@ use std::collections::HashMap; use std::ffi::OsStr; -use std::io::{self, BufRead}; +use std::net::TcpListener; use std::path::Path; use std::process::Command; -use std::sync::atomic::{AtomicU16, Ordering}; +use std::sync::atomic::AtomicU16; use anyhow::Context; -/// A counter for uniquely naming Ganache containers -static GANACHE_CONTAINER_COUNT: AtomicU16 = AtomicU16::new(0); -/// A counter for uniquely naming Postgres databases -static POSTGRES_DATABASE_COUNT: AtomicU16 = AtomicU16::new(0); -/// A counter for uniquely assigning ports. -static PORT_NUMBER_COUNTER: AtomicU16 = AtomicU16::new(10_000); - const POSTGRESQL_DEFAULT_PORT: u16 = 5432; const GANACHE_DEFAULT_PORT: u16 = 8545; const IPFS_DEFAULT_PORT: u16 = 5001; /// Maps `Service => Host` exposed ports. -#[derive(Debug)] -pub struct MappedPorts(pub HashMap); +pub type MappedPorts = HashMap; /// Strip parent directories from filenames pub fn basename(path: &impl AsRef) -> String { @@ -31,45 +23,44 @@ pub fn basename(path: &impl AsRef) -> String { .expect("failed to infer basename for path.") } -/// Fetches a unique number for naming Ganache containers -pub fn get_unique_ganache_counter() -> u16 { - increase_atomic_counter(&GANACHE_CONTAINER_COUNT) -} -/// Fetches a unique number for naming Postgres databases -pub fn get_unique_postgres_counter() -> u16 { - increase_atomic_counter(&POSTGRES_DATABASE_COUNT) -} -/// Fetches a unique port number -pub fn get_unique_port_number() -> u16 { - increase_atomic_counter(&PORT_NUMBER_COUNTER) +/// Parses stdout bytes into a prefixed String +pub fn pretty_output(blob: &[u8], prefix: &str) -> String { + blob.split(|b| *b == b'\n') + .map(String::from_utf8_lossy) + .map(|line| format!("{}{}", prefix, line)) + .collect::>() + .join("\n") } -fn increase_atomic_counter(counter: &'static AtomicU16) -> u16 { - let old_count = counter.fetch_add(1, Ordering::SeqCst); - old_count + 1 -} +/// Finds and returns a free port. Ports are never *guaranteed* to be free because of +/// [TOCTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use) race +/// conditions. +/// +/// This function guards against conflicts coming from other callers, so you +/// will only get port conflicts from external resources. +fn get_free_port() -> u16 { + // We start cycling through ports at 10000, which is high enough in the port + // space to to cause few conflicts. + const RANGE_START: u16 = 10_000; + static COUNTER: AtomicU16 = AtomicU16::new(RANGE_START); -/// Parses stdio bytes into a prefixed String -pub fn pretty_output(stdio: &[u8], prefix: &str) -> String { - let mut cursor = io::Cursor::new(stdio); - let mut buf = vec![]; - let mut string = String::new(); loop { - buf.clear(); - let bytes_read = cursor - .read_until(b'\n', &mut buf) - .expect("failed to read from stdio."); - if bytes_read == 0 { - break; + let ordering = std::sync::atomic::Ordering::SeqCst; + let port = COUNTER.fetch_add(1, ordering); + if port < RANGE_START { + // We've wrapped around, start over. + COUNTER.store(RANGE_START, ordering); + continue; + } + + let bind = TcpListener::bind(("127.0.0.1", port)); + if bind.is_ok() { + return port; } - let as_string = String::from_utf8_lossy(&buf); - string.push_str(&prefix); - string.push_str(&as_string); // will contain a newline } - string } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct GraphNodePorts { pub http: u16, pub index: u16, @@ -77,31 +68,23 @@ pub struct GraphNodePorts { pub admin: u16, pub metrics: u16, } + impl GraphNodePorts { - /// Returns five available port numbers, using dynamic port ranges - pub fn get_ports() -> GraphNodePorts { - let mut ports = [0u16; 5]; - for port in ports.iter_mut() { - let min = get_unique_port_number(); - let max = min + 1_000; - let free_port_in_range = port_check::free_local_port_in_range(min, max) - .expect("failed to obtain a free port in range"); - *port = free_port_in_range; - } - GraphNodePorts { - http: ports[0], - index: ports[1], - ws: ports[2], - admin: ports[3], - metrics: ports[4], + /// Populates all values with random free ports. + pub fn random_free() -> GraphNodePorts { + Self { + http: get_free_port(), + index: get_free_port(), + ws: get_free_port(), + admin: get_free_port(), + metrics: get_free_port(), } } } // Build a postgres connection string -pub fn make_postgres_uri(unique_id: &u16, postgres_ports: &MappedPorts) -> String { +pub fn make_postgres_uri(db_name: &str, postgres_ports: &MappedPorts) -> String { let port = postgres_ports - .0 .get(&POSTGRESQL_DEFAULT_PORT) .expect("failed to fetch Postgres port from mapped ports"); format!( @@ -110,13 +93,12 @@ pub fn make_postgres_uri(unique_id: &u16, postgres_ports: &MappedPorts) -> Strin password = "password", host = "localhost", port = port, - database_name = postgres_test_database_name(unique_id), + database_name = db_name, ) } pub fn make_ipfs_uri(ipfs_ports: &MappedPorts) -> String { let port = ipfs_ports - .0 .get(&IPFS_DEFAULT_PORT) .expect("failed to fetch IPFS port from mapped ports"); format!("http://{host}:{port}", host = "localhost", port = port) @@ -125,7 +107,6 @@ pub fn make_ipfs_uri(ipfs_ports: &MappedPorts) -> String { // Build a Ganache connection string. Returns the port number and the URI. pub fn make_ganache_uri(ganache_ports: &MappedPorts) -> (u16, String) { let port = ganache_ports - .0 .get(&GANACHE_DEFAULT_PORT) .expect("failed to fetch Ganache port from mapped ports"); let uri = format!("test:http://{host}:{port}", host = "localhost", port = port); @@ -136,10 +117,6 @@ pub fn contains_subslice(data: &[T], needle: &[T]) -> bool { data.windows(needle.len()).any(|w| w == needle) } -pub fn postgres_test_database_name(unique_id: &u16) -> String { - format!("test_database_{}", unique_id) -} - /// Returns captured stdout pub fn run_cmd(command: &mut Command) -> String { let program = command.get_program().to_str().unwrap().to_owned(); @@ -148,11 +125,11 @@ pub fn run_cmd(command: &mut Command) -> String { .context(format!("failed to run {}", program)) .unwrap(); println!( - "stdout {}", + "stdout:\n{}", pretty_output(&output.stdout, &format!("[{}:stdout] ", program)) ); println!( - "stderr {}", + "stderr:\n{}", pretty_output(&output.stderr, &format!("[{}:stderr] ", program)) ); diff --git a/tests/src/lib.rs b/tests/src/lib.rs index bdb39eeee34..3a3b4a87659 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,2 +1,3 @@ +pub mod docker_utils; pub mod fixture; pub mod helpers; diff --git a/tests/tests/common/docker.rs b/tests/tests/common/docker.rs deleted file mode 100644 index 725b40232af..00000000000 --- a/tests/tests/common/docker.rs +++ /dev/null @@ -1,289 +0,0 @@ -use bollard::image::CreateImageOptions; -use bollard::models::HostConfig; -use bollard::{container, Docker}; -use graph_tests::helpers::{contains_subslice, postgres_test_database_name, MappedPorts}; -use std::collections::HashMap; -use tokio::time::{sleep, Duration}; -use tokio_stream::StreamExt; - -const POSTGRES_IMAGE: &str = "postgres:latest"; -const IPFS_IMAGE: &str = "ipfs/go-ipfs:v0.10.0"; -const GANACHE_IMAGE: &str = "trufflesuite/ganache-cli:latest"; -type DockerError = bollard::errors::Error; - -pub async fn pull_images() { - use tokio_stream::StreamMap; - - let client = - Docker::connect_with_local_defaults().expect("Failed to connect to docker daemon."); - - let images = [POSTGRES_IMAGE, IPFS_IMAGE, GANACHE_IMAGE]; - let mut map = StreamMap::new(); - - for image_name in &images { - let options = Some(CreateImageOptions { - from_image: *image_name, - ..Default::default() - }); - let stream = client.create_image(options, None, None); - map.insert(*image_name, stream); - } - - while let Some(message) = map.next().await { - if let (key, Err(msg)) = message { - panic!("Error when pulling docker image for {}: {}", key, msg) - } - } -} - -pub async fn stop_and_remove(client: &Docker, service_name: &str) -> Result<(), DockerError> { - client.kill_container::<&str>(service_name, None).await?; - client.remove_container(service_name, None).await -} - -/// Represents all possible service containers to be spawned -#[derive(Debug)] -pub enum TestContainerService { - Postgres, - Ipfs, - Ganache(u16), -} - -impl TestContainerService { - fn config(&self) -> container::Config<&'static str> { - use TestContainerService::*; - match self { - Postgres => Self::build_postgres_container_config(), - Ipfs => Self::build_ipfs_container_config(), - Ganache(_u32) => Self::build_ganache_container_config(), - } - } - - fn options(&self) -> container::CreateContainerOptions { - container::CreateContainerOptions { name: self.name() } - } - - fn name(&self) -> String { - use TestContainerService::*; - match self { - Postgres => "graph_node_integration_test_postgres".into(), - Ipfs => "graph_node_integration_test_ipfs".into(), - Ganache(container_count) => { - format!("graph_node_integration_test_ganache_{}", container_count) - } - } - } - - fn build_postgres_container_config() -> container::Config<&'static str> { - let host_config = HostConfig { - publish_all_ports: Some(true), - ..Default::default() - }; - - container::Config { - image: Some(POSTGRES_IMAGE), - env: Some(vec![ - "POSTGRES_PASSWORD=password", - "POSTGRES_USER=postgres", - "POSTGRES_INITDB_ARGS=-E UTF8 --locale=C", - ]), - host_config: Some(host_config), - cmd: Some(vec![ - "postgres", - "-N", - "1000", - "-cshared_preload_libraries=pg_stat_statements", - ]), - ..Default::default() - } - } - - fn build_ipfs_container_config() -> container::Config<&'static str> { - let host_config = HostConfig { - publish_all_ports: Some(true), - ..Default::default() - }; - - container::Config { - image: Some(IPFS_IMAGE), - host_config: Some(host_config), - ..Default::default() - } - } - - fn build_ganache_container_config() -> container::Config<&'static str> { - let host_config = HostConfig { - publish_all_ports: Some(true), - ..Default::default() - }; - - container::Config { - image: Some(GANACHE_IMAGE), - cmd: Some(vec!["-d", "-l", "100000000000", "-g", "1"]), - host_config: Some(host_config), - ..Default::default() - } - } -} - -/// Handles the connection to the docker daemon and keeps track the service running inside it. -pub struct DockerTestClient { - service: TestContainerService, - client: Docker, -} - -impl DockerTestClient { - pub async fn start(service: TestContainerService) -> Result { - let client = - Docker::connect_with_local_defaults().expect("Failed to connect to docker daemon."); - - let docker_test_client = Self { service, client }; - - // try to remove the container if it already exists - let _ = stop_and_remove( - &docker_test_client.client, - &docker_test_client.service.name(), - ) - .await; - - // create docker container - docker_test_client - .client - .create_container( - Some(docker_test_client.service.options()), - docker_test_client.service.config(), - ) - .await?; - - // start docker container - docker_test_client - .client - .start_container::<&'static str>(&docker_test_client.service.name(), None) - .await?; - - Ok(docker_test_client) - } - - pub async fn stop(&self) -> Result<(), DockerError> { - stop_and_remove(&self.client, &self.service.name()).await - } - - pub async fn exposed_ports(&self) -> Result { - use bollard::models::ContainerSummaryInner; - let mut filters = HashMap::new(); - filters.insert("name".to_string(), vec![self.service.name()]); - let options = Some(container::ListContainersOptions { - filters, - limit: Some(1), - ..Default::default() - }); - let results = self.client.list_containers(options).await?; - let ports = match &results.as_slice() { - &[ContainerSummaryInner { - ports: Some(ports), .. - }] => ports, - unexpected_response => panic!( - "Received a unexpected_response from docker API: {:#?}", - unexpected_response - ), - }; - let mapped_ports: MappedPorts = to_mapped_ports(ports.to_vec()); - Ok(mapped_ports) - } - - /// halts execution until a trigger message is detected on stdout or, optionally, - /// waits for a specified amount of time after the message appears. - pub async fn wait_for_message( - &self, - trigger_message: &[u8], - hard_wait: &Option, - ) -> Result<&Self, DockerError> { - // listen to container logs - let mut stream = self.client.logs::( - &self.service.name(), - Some(container::LogsOptions { - follow: true, - stdout: true, - stderr: true, - ..Default::default() - }), - ); - - // halt execution until a message is received - loop { - match stream.next().await { - Some(Ok(container::LogOutput::StdOut { message })) => { - if contains_subslice(&message, trigger_message) { - break; - } else { - sleep(Duration::from_millis(100)).await; - } - } - Some(Err(error)) => return Err(error), - None => { - panic!("stream ended before expected message could be detected") - } - _ => {} - } - } - - if let Some(seconds) = hard_wait { - sleep(Duration::from_secs(*seconds)).await; - } - Ok(self) - } - - /// Calls `docker exec` on the container to create a test database. - pub async fn create_postgres_database( - docker: &DockerTestClient, - unique_id: &u16, - ) -> Result<(), DockerError> { - use bollard::exec; - - let database_name = postgres_test_database_name(unique_id); - - // 1. Create Exec - let config = exec::CreateExecOptions { - cmd: Some(vec!["createdb", "-E", "UTF8", "--locale=C", &database_name]), - user: Some("postgres"), - attach_stdout: Some(true), - ..Default::default() - }; - - let message = docker - .client - .create_exec(&docker.service.name(), config) - .await?; - - // 2. Start Exec - let mut stream = docker.client.start_exec(&message.id, None); - while let Some(_) = stream.next().await { /* consume stream */ } - - // 3. Inspecet exec - let inspect = docker.client.inspect_exec(&message.id).await?; - if let Some(0) = inspect.exit_code { - Ok(()) - } else { - panic!("failed to run 'createdb' command using docker exec"); - } - } -} - -fn to_mapped_ports(input: Vec) -> MappedPorts { - let mut hashmap = HashMap::new(); - - for port in &input { - if let bollard::models::Port { - private_port, - public_port: Some(public_port), - .. - } = port - { - hashmap.insert(*private_port as u16, *public_port as u16); - } - } - if hashmap.is_empty() { - panic!("Container exposed no ports. Input={:?}", input) - } - MappedPorts(hashmap) -} diff --git a/tests/tests/common/mod.rs b/tests/tests/common/mod.rs deleted file mode 100644 index 18c50280380..00000000000 --- a/tests/tests/common/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod docker; diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs new file mode 100644 index 00000000000..2f11094d754 --- /dev/null +++ b/tests/tests/integration_tests.rs @@ -0,0 +1,501 @@ +//! Containeraized integration tests. +//! +//! # On the use of [`tokio::join!`] +//! +//! While linear `.await`s look best, sometimes we don't particularly care +//! about the order of execution and we can thus reduce test execution times by +//! `.await`ing in parallel. [`tokio::join!`] and similar macros can help us +//! with that, at the cost of some readability. As a general rule only a few +//! tasks are really worth parallelizing, and applying this trick +//! indiscriminately will only result in messy code and diminishing returns. + +use anyhow::Context; +use futures::{StreamExt, TryStreamExt}; +use graph_tests::docker_utils::{pull_service_images, ServiceContainer, ServiceContainerKind}; +use graph_tests::helpers::{ + basename, make_ganache_uri, make_ipfs_uri, make_postgres_uri, pretty_output, GraphNodePorts, + MappedPorts, +}; +use std::fs; +use std::num::NonZeroUsize; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::Duration; +use tokio::io::AsyncReadExt; +use tokio::process::{Child, Command}; + +/// All directories containing integration tests to run. +/// +/// Hardcoding these paths seems "wrong", and we very well could obtain this +/// list with some directory listing magic. That would, however, also +/// require us to filter out `node_modules`, support files, etc.. Hardly worth +/// it. +pub const INTEGRATION_TEST_DIRS: &[&str] = &[ + "api-version-v0-0-4", + "ganache-reverts", + "host-exports", + "non-fatal-errors", + "overloaded-contract-functions", + "poi-for-failed-subgraph", + "remove-then-update", + "value-roundtrip", +]; + +#[derive(Debug, Clone)] +struct IntegrationTestSettings { + n_parallel_tests: u64, + ganache_hard_wait: Duration, + ipfs_hard_wait: Duration, + postgres_hard_wait: Duration, +} + +impl IntegrationTestSettings { + /// Automatically fills in missing env. vars. with defaults. + /// + /// # Panics + /// + /// Panics if any of the env. vars. is set incorrectly. + pub fn from_env() -> Self { + Self { + n_parallel_tests: parse_numeric_environment_variable("N_CONCURRENT_TESTS").unwrap_or( + // Lots of I/O going on in these tests, so we spawn twice as + // many jobs as suggested. + 2 * std::thread::available_parallelism() + .map(NonZeroUsize::get) + .unwrap_or(2) as u64, + ), + ganache_hard_wait: Duration::from_secs( + parse_numeric_environment_variable("TESTS_GANACHE_HARD_WAIT_SECONDS").unwrap_or(0), + ), + ipfs_hard_wait: Duration::from_secs( + parse_numeric_environment_variable("TESTS_IPFS_HARD_WAIT_SECONDS").unwrap_or(0), + ), + postgres_hard_wait: Duration::from_secs( + parse_numeric_environment_variable("TESTS_POSTGRES_HARD_WAIT_SECONDS").unwrap_or(0), + ), + } + } +} + +/// An aggregator of all configuration and settings required to run a single +/// integration test. +#[derive(Debug)] +struct IntegrationTestRecipe { + postgres_uri: String, + ipfs_uri: String, + ganache_port: u16, + ganache_uri: String, + graph_node_ports: GraphNodePorts, + graph_node_bin: Arc, + test_directory: PathBuf, +} + +impl IntegrationTestRecipe { + fn test_name(&self) -> String { + basename(&self.test_directory) + } + + fn graph_node_admin_uri(&self) -> String { + format!("http://localhost:{}/", self.graph_node_ports.admin) + } +} + +/// Info about a finished test command +#[derive(Debug)] +struct IntegrationTestResult { + success: bool, + _exit_status_code: Option, + output: Output, +} + +#[derive(Debug)] +struct Output { + stdout: Option, + stderr: Option, +} + +impl std::fmt::Display for Output { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(ref stdout) = self.stdout { + write!(f, "{}", stdout)?; + } + if let Some(ref stderr) = self.stderr { + write!(f, "{}", stderr)? + } + Ok(()) + } +} + +// The results of a finished integration test +#[derive(Debug)] +struct IntegrationTestSummary { + test_recipe: IntegrationTestRecipe, + test_command_result: IntegrationTestResult, + graph_node_output: Output, +} + +impl IntegrationTestSummary { + fn print_outcome(&self) { + let status = match self.test_command_result.success { + true => "SUCCESS", + false => "FAILURE", + }; + println!("- Test: {}: {}", status, self.test_recipe.test_name()) + } + + fn print_failure(&self) { + if self.test_command_result.success { + return; + } + let test_name = self.test_recipe.test_name(); + println!("============="); + println!("\nFailed test: {}", test_name); + println!("-------------"); + println!("{:#?}", self.test_recipe); + println!("-------------"); + println!("\nFailed test command output:"); + println!("---------------------------"); + println!("{}", self.test_command_result.output); + println!("--------------------------"); + println!("graph-node command output:"); + println!("--------------------------"); + println!("{}", self.graph_node_output); + } +} + +/// The main test entrypoint. +#[tokio::test] +async fn parallel_integration_tests() -> anyhow::Result<()> { + let test_settings = IntegrationTestSettings::from_env(); + + let current_working_dir = + std::env::current_dir().context("failed to identify working directory")?; + let yarn_workspace_dir = current_working_dir.join("integration-tests"); + let test_dirs = INTEGRATION_TEST_DIRS + .iter() + .map(|p| yarn_workspace_dir.join(PathBuf::from(p))) + .collect::>(); + + // Show discovered tests. + println!("Found {} integration test(s):", test_dirs.len()); + for dir in &test_dirs { + println!(" - {}", basename(dir)); + } + + tokio::join!( + // Pull the required Docker images. + pull_service_images(), + // Run `yarn` command to build workspace. + run_yarn_command(&yarn_workspace_dir), + ); + + println!("Starting PostgreSQL and IPFS containers..."); + + // Not only do we start the containers, but we also need to wait for + // them to be up and running and ready to accept connections. + let (postgres, ipfs) = tokio::try_join!( + ServiceDependency::start( + ServiceContainerKind::Postgres, + "database system is ready to accept connections", + test_settings.postgres_hard_wait + ), + ServiceDependency::start( + ServiceContainerKind::Ipfs, + "Daemon is ready", + test_settings.ipfs_hard_wait + ), + )?; + + println!( + "Containers are ready! Running tests with N_CONCURRENT_TESTS={} ...", + test_settings.n_parallel_tests + ); + + let graph_node = Arc::new( + fs::canonicalize("../target/debug/graph-node") + .context("failed to infer `graph-node` program location. (Was it built already?)")?, + ); + + let stream = tokio_stream::iter(test_dirs) + .map(|dir| { + run_integration_test( + dir, + postgres.clone(), + ipfs.clone(), + graph_node.clone(), + test_settings.ganache_hard_wait, + ) + }) + .buffered(test_settings.n_parallel_tests as usize); + + let test_results: Vec = stream.try_collect().await?; + let failed = test_results.iter().any(|r| !r.test_command_result.success); + + // All tests have finished; we don't need the containers anymore. + tokio::try_join!( + async { + postgres + .container + .stop() + .await + .context("failed to stop container with Postgres") + }, + async { + ipfs.container + .stop() + .await + .context("failed to stop container with IPFS") + }, + )?; + + // print failures + for failed_test in test_results + .iter() + .filter(|t| !t.test_command_result.success) + { + failed_test.print_failure() + } + + // print test result summary + println!("\nTest results:"); + for test_result in &test_results { + test_result.print_outcome() + } + + if failed { + Err(anyhow::anyhow!("Some tests have failed")) + } else { + Ok(()) + } +} + +#[derive(Clone)] +struct ServiceDependency { + container: Arc, + ports: Arc, +} + +impl ServiceDependency { + async fn start( + service: ServiceContainerKind, + wait_msg: &str, + hard_wait: Duration, + ) -> anyhow::Result { + let service = ServiceContainer::start(service).await.context(format!( + "Failed to start container service `{}`", + service.name() + ))?; + + service + .wait_for_message(wait_msg.as_bytes(), hard_wait) + .await + .context(format!( + "failed to wait for {} container to be ready to accept connections", + service.container_name() + ))?; + + let ports = service.exposed_ports().await.context(format!( + "failed to obtain exposed ports for the `{}` container", + service.container_name() + ))?; + + Ok(Self { + container: Arc::new(service), + ports: Arc::new(ports), + }) + } +} + +/// Prepare and run the integration test +async fn run_integration_test( + test_directory: PathBuf, + postgres: ServiceDependency, + ipfs: ServiceDependency, + graph_node_bin: Arc, + ganache_hard_wait: Duration, +) -> anyhow::Result { + let db_name = + format!("{}-{}", basename(&test_directory), uuid::Uuid::new_v4()).replace("-", "_"); + + let (ganache, _) = tokio::try_join!( + // Start a dedicated Ganache container for this test. + async { + ServiceDependency::start( + ServiceContainerKind::Ganache, + "Listening on ", + ganache_hard_wait, + ) + .await + .context("failed to start Ganache container") + }, + // PostgreSQL is up and running, but we still need to create the database. + async { + ServiceContainer::create_postgres_database(&postgres.container, &db_name) + .await + .context("failed to create the test database.") + } + )?; + + // Build URIs. + let postgres_uri = { make_postgres_uri(&db_name, &postgres.ports) }; + let ipfs_uri = make_ipfs_uri(&ipfs.ports); + let (ganache_port, ganache_uri) = make_ganache_uri(&ganache.ports); + + let test_recipe = IntegrationTestRecipe { + postgres_uri, + ipfs_uri, + ganache_uri, + ganache_port, + graph_node_bin, + graph_node_ports: GraphNodePorts::random_free(), + test_directory, + }; + + // Spawn graph-node. + let mut graph_node_child_command = run_graph_node(&test_recipe)?; + + println!("Test started: {}", basename(&test_recipe.test_directory)); + let result = run_test_command(&test_recipe).await?; + + let (graph_node_output, _) = tokio::try_join!( + async { + // Stop graph-node and read its output. + stop_graph_node(&mut graph_node_child_command) + .await + .context("failed to stop graph-node") + }, + async { + // Stop Ganache. + ganache + .container + .stop() + .await + .context("failed to stop container service for Ganache") + } + )?; + + Ok(IntegrationTestSummary { + test_recipe, + test_command_result: result, + graph_node_output, + }) +} + +/// Runs a command for a integration test +async fn run_test_command( + test_recipe: &IntegrationTestRecipe, +) -> anyhow::Result { + let output = Command::new("yarn") + .arg("test") + .env("GANACHE_TEST_PORT", test_recipe.ganache_port.to_string()) + .env("GRAPH_NODE_ADMIN_URI", test_recipe.graph_node_admin_uri()) + .env( + "GRAPH_NODE_HTTP_PORT", + test_recipe.graph_node_ports.http.to_string(), + ) + .env( + "GRAPH_NODE_INDEX_PORT", + test_recipe.graph_node_ports.index.to_string(), + ) + .env("IPFS_URI", &test_recipe.ipfs_uri) + .current_dir(&test_recipe.test_directory) + .output() + .await + .context("failed to run test command")?; + + let test_name = test_recipe.test_name(); + let stdout_tag = format!("[{}:stdout] ", test_name); + let stderr_tag = format!("[{}:stderr] ", test_name); + + Ok(IntegrationTestResult { + _exit_status_code: output.status.code(), + success: output.status.success(), + output: Output { + stdout: Some(pretty_output(&output.stdout, &stdout_tag)), + stderr: Some(pretty_output(&output.stderr, &stderr_tag)), + }, + }) +} + +fn run_graph_node(recipe: &IntegrationTestRecipe) -> anyhow::Result { + use std::process::Stdio; + + let mut command = Command::new(recipe.graph_node_bin.as_os_str()); + command + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .arg("--postgres-url") + .arg(&recipe.postgres_uri) + .arg("--ethereum-rpc") + .arg(&recipe.ganache_uri) + .arg("--ipfs") + .arg(&recipe.ipfs_uri) + .arg("--http-port") + .arg(recipe.graph_node_ports.http.to_string()) + .arg("--index-node-port") + .arg(recipe.graph_node_ports.index.to_string()) + .arg("--ws-port") + .arg(recipe.graph_node_ports.ws.to_string()) + .arg("--admin-port") + .arg(recipe.graph_node_ports.admin.to_string()) + .arg("--metrics-port") + .arg(recipe.graph_node_ports.metrics.to_string()); + + command + .spawn() + .context("failed to start graph-node command.") +} + +async fn stop_graph_node(child: &mut Child) -> anyhow::Result { + child.kill().await.context("Failed to kill graph-node")?; + + // capture stdio + let stdout = match child.stdout.take() { + Some(mut data) => Some(process_stdio(&mut data, "[graph-node:stdout] ").await?), + None => None, + }; + let stderr = match child.stderr.take() { + Some(mut data) => Some(process_stdio(&mut data, "[graph-node:stderr] ").await?), + None => None, + }; + + Ok(Output { stdout, stderr }) +} + +async fn process_stdio( + stdio: &mut T, + prefix: &str, +) -> anyhow::Result { + let mut buffer: Vec = Vec::new(); + stdio + .read_to_end(&mut buffer) + .await + .context("failed to read stdio")?; + Ok(pretty_output(&buffer, prefix)) +} + +/// run yarn to build everything +async fn run_yarn_command(base_directory: &impl AsRef) { + let timer = std::time::Instant::now(); + println!("Running `yarn` command in integration tests root directory."); + let output = Command::new("yarn") + .current_dir(base_directory) + .output() + .await + .expect("failed to run yarn command"); + + if output.status.success() { + println!("`yarn` command finished in {}s", timer.elapsed().as_secs()); + return; + } + println!("Yarn command failed."); + println!("{}", pretty_output(&output.stdout, "[yarn:stdout]")); + println!("{}", pretty_output(&output.stderr, "[yarn:stderr]")); + panic!("Yarn command failed.") +} + +fn parse_numeric_environment_variable(environment_variable_name: &str) -> Option { + std::env::var(environment_variable_name) + .ok() + .and_then(|x| x.parse().ok()) +} diff --git a/tests/tests/parallel_tests.rs b/tests/tests/parallel_tests.rs deleted file mode 100644 index 54a37ac51f8..00000000000 --- a/tests/tests/parallel_tests.rs +++ /dev/null @@ -1,435 +0,0 @@ -mod common; -use anyhow::Context; -use common::docker::{pull_images, DockerTestClient, TestContainerService}; -use futures::StreamExt; -use graph_tests::helpers::{ - basename, get_unique_ganache_counter, get_unique_postgres_counter, make_ganache_uri, - make_ipfs_uri, make_postgres_uri, pretty_output, GraphNodePorts, MappedPorts, -}; -use std::fs; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use tokio::io::AsyncReadExt; -use tokio::process::{Child, Command}; - -const DEFAULT_N_CONCURRENT_TESTS: usize = 15; - -lazy_static::lazy_static! { - static ref GANACHE_HARD_WAIT_SECONDS: Option = - parse_numeric_environment_variable("TESTS_GANACHE_HARD_WAIT_SECONDS"); - static ref IPFS_HARD_WAIT_SECONDS: Option = - parse_numeric_environment_variable("TESTS_IPFS_HARD_WAIT_SECONDS"); - static ref POSTGRES_HARD_WAIT_SECONDS: Option = - parse_numeric_environment_variable("TESTS_POSTGRES_HARD_WAIT_SECONDS"); -} - -/// All integration tests subdirectories to run -pub const INTEGRATION_TESTS_DIRECTORIES: [&str; 8] = [ - "api-version-v0-0-4", - "ganache-reverts", - "host-exports", - "non-fatal-errors", - "overloaded-contract-functions", - "poi-for-failed-subgraph", - "remove-then-update", - "value-roundtrip", -]; - -/// Contains all information a test command needs -#[derive(Debug)] -struct IntegrationTestSetup { - postgres_uri: String, - ipfs_uri: String, - ganache_port: u16, - ganache_uri: String, - graph_node_ports: GraphNodePorts, - graph_node_bin: Arc, - test_directory: PathBuf, -} - -impl IntegrationTestSetup { - fn test_name(&self) -> String { - basename(&self.test_directory) - } - - fn graph_node_admin_uri(&self) -> String { - let ws_port = self.graph_node_ports.admin; - format!("http://localhost:{}/", ws_port) - } -} - -/// Info about a finished test command -#[derive(Debug)] -struct TestCommandResults { - success: bool, - _exit_code: Option, - stdout: String, - stderr: String, -} - -#[derive(Debug)] -struct StdIO { - stdout: Option, - stderr: Option, -} -impl std::fmt::Display for StdIO { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(ref stdout) = self.stdout { - write!(f, "{}", stdout)?; - } - if let Some(ref stderr) = self.stderr { - write!(f, "{}", stderr)? - } - Ok(()) - } -} - -// The results of a finished integration test -#[derive(Debug)] -struct IntegrationTestResult { - test_setup: IntegrationTestSetup, - test_command_results: TestCommandResults, - graph_node_stdio: StdIO, -} - -impl IntegrationTestResult { - fn print_outcome(&self) { - let status = match self.test_command_results.success { - true => "SUCCESS", - false => "FAILURE", - }; - println!("- Test: {}: {}", status, self.test_setup.test_name()) - } - - fn print_failure(&self) { - if self.test_command_results.success { - return; - } - let test_name = self.test_setup.test_name(); - println!("============="); - println!("\nFailed test: {}", test_name); - println!("-------------"); - println!("{:#?}", self.test_setup); - println!("-------------"); - println!("\nFailed test command output:"); - println!("---------------------------"); - println!("{}", self.test_command_results.stdout); - println!("{}", self.test_command_results.stderr); - println!("--------------------------"); - println!("graph-node command output:"); - println!("--------------------------"); - println!("{}", self.graph_node_stdio); - } -} - -/// The main test entrypoint -#[tokio::test] -async fn parallel_integration_tests() -> anyhow::Result<()> { - // use a environment variable for limiting the number of concurrent tests - let n_parallel_tests: usize = std::env::var("N_CONCURRENT_TESTS") - .ok() - .and_then(|x| x.parse().ok()) - .unwrap_or(DEFAULT_N_CONCURRENT_TESTS); - - let current_working_directory = - std::env::current_dir().context("failed to identify working directory")?; - let integration_tests_root_directory = current_working_directory.join("integration-tests"); - - // pull required docker images - pull_images().await; - - let test_directories = INTEGRATION_TESTS_DIRECTORIES - .iter() - .map(|ref p| integration_tests_root_directory.join(PathBuf::from(p))) - .collect::>(); - - // Show discovered tests - println!("Found {} integration tests:", test_directories.len()); - for dir in &test_directories { - println!(" - {}", basename(dir)); - } - - // run `yarn` command to build workspace - run_yarn_command(&integration_tests_root_directory).await; - - // start docker containers for Postgres and IPFS and wait for them to be ready - let postgres = Arc::new( - DockerTestClient::start(TestContainerService::Postgres) - .await - .context("failed to start container service for Postgres.")?, - ); - postgres - .wait_for_message( - b"database system is ready to accept connections", - &*POSTGRES_HARD_WAIT_SECONDS, - ) - .await - .context("failed to wait for Postgres container to be ready to accept connections")?; - - let ipfs = DockerTestClient::start(TestContainerService::Ipfs) - .await - .context("failed to start container service for IPFS.")?; - ipfs.wait_for_message(b"Daemon is ready", &*IPFS_HARD_WAIT_SECONDS) - .await - .context("failed to wait for Ipfs container to be ready to accept connections")?; - - let postgres_ports = Arc::new( - postgres - .exposed_ports() - .await - .context("failed to obtain exposed ports for the Postgres container")?, - ); - let ipfs_ports = Arc::new( - ipfs.exposed_ports() - .await - .context("failed to obtain exposed ports for the IPFS container")?, - ); - - let graph_node = Arc::new( - fs::canonicalize("../target/debug/graph-node") - .context("failed to infer `graph-node` program location. (Was it built already?)")?, - ); - - // run tests - let mut test_results = Vec::new(); - - let mut stream = tokio_stream::iter(test_directories) - .map(|dir| { - run_integration_test( - dir, - postgres.clone(), - postgres_ports.clone(), - ipfs_ports.clone(), - graph_node.clone(), - ) - }) - .buffered(n_parallel_tests); - - let mut failed = false; - while let Some(test_result) = stream.next().await { - let test_result = test_result?; - if !test_result.test_command_results.success { - failed = true; - } - test_results.push(test_result); - } - - // Stop containers. - postgres - .stop() - .await - .context("failed to stop container service for Postgres")?; - ipfs.stop() - .await - .context("failed to stop container service for IPFS")?; - - // print failures - for failed_test in test_results - .iter() - .filter(|t| !t.test_command_results.success) - { - failed_test.print_failure() - } - - // print test result summary - println!("\nTest results:"); - for test_result in &test_results { - test_result.print_outcome() - } - - if failed { - Err(anyhow::anyhow!("Some tests have failed")) - } else { - Ok(()) - } -} - -/// Prepare and run the integration test -async fn run_integration_test( - test_directory: PathBuf, - postgres_docker: Arc, - postgres_ports: Arc, - ipfs_ports: Arc, - graph_node_bin: Arc, -) -> anyhow::Result { - // start a dedicated ganache container for this test - let unique_ganache_counter = get_unique_ganache_counter(); - let ganache = DockerTestClient::start(TestContainerService::Ganache(unique_ganache_counter)) - .await - .context("failed to start container service for Ganache.")?; - ganache - .wait_for_message(b"Listening on ", &*GANACHE_HARD_WAIT_SECONDS) - .await - .context("failed to wait for Ganache container to be ready to accept connections")?; - - let ganache_ports: MappedPorts = ganache - .exposed_ports() - .await - .context("failed to obtain exposed ports for Ganache container")?; - - // build URIs - let postgres_unique_id = get_unique_postgres_counter(); - - let postgres_uri = make_postgres_uri(&postgres_unique_id, &postgres_ports); - let ipfs_uri = make_ipfs_uri(&ipfs_ports); - let (ganache_port, ganache_uri) = make_ganache_uri(&ganache_ports); - - // create test database - DockerTestClient::create_postgres_database(&postgres_docker, &postgres_unique_id) - .await - .context("failed to create the test database.")?; - - // prepare to run test comand - let test_setup = IntegrationTestSetup { - postgres_uri, - ipfs_uri, - ganache_uri, - ganache_port, - graph_node_bin, - graph_node_ports: GraphNodePorts::get_ports(), - test_directory, - }; - - // spawn graph-node - let mut graph_node_child_command = run_graph_node(&test_setup).await?; - - println!("Test started: {}", basename(&test_setup.test_directory)); - let test_command_results = run_test_command(&test_setup).await?; - - // stop graph-node - - let graph_node_stdio = stop_graph_node(&mut graph_node_child_command).await?; - // stop ganache container - ganache - .stop() - .await - .context("failed to stop container service for Ganache")?; - - Ok(IntegrationTestResult { - test_setup, - test_command_results, - graph_node_stdio, - }) -} - -/// Runs a command for a integration test -async fn run_test_command(test_setup: &IntegrationTestSetup) -> anyhow::Result { - let output = Command::new("yarn") - .arg("test") - .env("GANACHE_TEST_PORT", test_setup.ganache_port.to_string()) - .env("GRAPH_NODE_ADMIN_URI", test_setup.graph_node_admin_uri()) - .env( - "GRAPH_NODE_HTTP_PORT", - test_setup.graph_node_ports.http.to_string(), - ) - .env( - "GRAPH_NODE_INDEX_PORT", - test_setup.graph_node_ports.index.to_string(), - ) - .env("IPFS_URI", &test_setup.ipfs_uri) - .current_dir(&test_setup.test_directory) - .output() - .await - .context("failed to run test command")?; - - let test_name = test_setup.test_name(); - let stdout_tag = format!("[{}:stdout] ", test_name); - let stderr_tag = format!("[{}:stderr] ", test_name); - - Ok(TestCommandResults { - success: output.status.success(), - _exit_code: output.status.code(), - stdout: pretty_output(&output.stdout, &stdout_tag), - stderr: pretty_output(&output.stderr, &stderr_tag), - }) -} -async fn run_graph_node(test_setup: &IntegrationTestSetup) -> anyhow::Result { - use std::process::Stdio; - - let mut command = Command::new(test_setup.graph_node_bin.as_os_str()); - command - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - // postgres - .arg("--postgres-url") - .arg(&test_setup.postgres_uri) - // ethereum - .arg("--ethereum-rpc") - .arg(&test_setup.ganache_uri) - // ipfs - .arg("--ipfs") - .arg(&test_setup.ipfs_uri) - // http port - .arg("--http-port") - .arg(test_setup.graph_node_ports.http.to_string()) - // index node port - .arg("--index-node-port") - .arg(test_setup.graph_node_ports.index.to_string()) - // ws port - .arg("--ws-port") - .arg(test_setup.graph_node_ports.ws.to_string()) - // admin port - .arg("--admin-port") - .arg(test_setup.graph_node_ports.admin.to_string()) - // metrics port - .arg("--metrics-port") - .arg(test_setup.graph_node_ports.metrics.to_string()); - - command - .spawn() - .context("failed to start graph-node command.") -} - -async fn stop_graph_node(child: &mut Child) -> anyhow::Result { - child.kill().await.context("Failed to kill graph-node")?; - - // capture stdio - let stdout = match child.stdout.take() { - Some(mut data) => Some(process_stdio(&mut data, "[graph-node:stdout] ").await?), - None => None, - }; - let stderr = match child.stderr.take() { - Some(mut data) => Some(process_stdio(&mut data, "[graph-node:stderr] ").await?), - None => None, - }; - - Ok(StdIO { stdout, stderr }) -} - -async fn process_stdio( - stdio: &mut T, - prefix: &str, -) -> anyhow::Result { - let mut buffer: Vec = Vec::new(); - stdio - .read_to_end(&mut buffer) - .await - .context("failed to read stdio")?; - Ok(pretty_output(&buffer, prefix)) -} - -/// run yarn to build everything -async fn run_yarn_command(base_directory: &impl AsRef) { - let timer = std::time::Instant::now(); - println!("Running `yarn` command in integration tests root directory."); - let output = Command::new("yarn") - .current_dir(base_directory) - .output() - .await - .expect("failed to run yarn command"); - - if output.status.success() { - println!("`yarn` command finished in {}s", timer.elapsed().as_secs()); - return; - } - println!("Yarn command failed."); - println!("{}", pretty_output(&output.stdout, "[yarn:stdout]")); - println!("{}", pretty_output(&output.stderr, "[yarn:stderr]")); - panic!("Yarn command failed.") -} - -fn parse_numeric_environment_variable(environment_variable_name: &str) -> Option { - std::env::var(environment_variable_name) - .ok() - .and_then(|x| x.parse().ok()) -} diff --git a/tests/tests/runner.rs b/tests/tests/runner_tests.rs similarity index 81% rename from tests/tests/runner.rs rename to tests/tests/runner_tests.rs index 52b422da03b..f3baf8a6725 100644 --- a/tests/tests/runner.rs +++ b/tests/tests/runner_tests.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::process::Command; use std::sync::atomic::{self, AtomicBool}; use std::sync::Arc; use std::time::Duration; @@ -9,24 +10,50 @@ use graph::blockchain::{Block, BlockPtr, Blockchain}; use graph::data::subgraph::schema::{SubgraphError, SubgraphHealth}; use graph::data_source::CausalityRegion; use graph::env::EnvVars; +use graph::ipfs_client::IpfsClient; use graph::object; use graph::prelude::ethabi::ethereum_types::H256; -use graph::prelude::{CheapClone, SubgraphAssignmentProvider, SubgraphName, SubgraphStore}; +use graph::prelude::{ + CheapClone, DeploymentHash, SubgraphAssignmentProvider, SubgraphName, SubgraphStore, +}; use graph_tests::fixture::ethereum::{chain, empty_block, genesis, push_test_log}; use graph_tests::fixture::{ - self, stores, test_ptr, test_ptr_reorged, MockAdapterSelector, NoopAdapterSelector, + self, stores, test_ptr, test_ptr_reorged, MockAdapterSelector, NoopAdapterSelector, Stores, }; +use graph_tests::helpers::run_cmd; use slog::{o, Discard, Logger}; +struct RunnerTestRecipe { + stores: Stores, + subgraph_name: SubgraphName, + hash: DeploymentHash, +} + +impl RunnerTestRecipe { + async fn new(subgraph_name: &str) -> Self { + let subgraph_name = SubgraphName::new(subgraph_name).unwrap(); + let test_dir = format!("./runner-tests/{}", subgraph_name); + + let (stores, hash) = tokio::join!( + stores("./runner-tests/config.simple.toml"), + build_subgraph(&test_dir) + ); + + Self { + stores, + subgraph_name, + hash, + } + } +} + #[tokio::test] async fn data_source_revert() -> anyhow::Result<()> { - let stores = stores("./integration-tests/config.simple.toml").await; - - let subgraph_name = SubgraphName::new("data-source-revert").unwrap(); - let hash = { - let test_dir = format!("./integration-tests/{}", subgraph_name); - fixture::build_subgraph(&test_dir).await - }; + let RunnerTestRecipe { + stores, + subgraph_name, + hash, + } = RunnerTestRecipe::new("data-source-revert").await; let blocks = { let block0 = genesis(); @@ -55,11 +82,9 @@ async fn data_source_revert() -> anyhow::Result<()> { // Test grafted version let subgraph_name = SubgraphName::new("data-source-revert-grafted").unwrap(); - let hash = fixture::build_subgraph_with_yarn_cmd( - "./integration-tests/data-source-revert", - "deploy:test-grafted", - ) - .await; + let hash = + build_subgraph_with_yarn_cmd("./runner-tests/data-source-revert", "deploy:test-grafted") + .await; let graft_block = Some(test_ptr(3)); let ctx = fixture::setup( subgraph_name.clone(), @@ -91,12 +116,11 @@ async fn data_source_revert() -> anyhow::Result<()> { #[tokio::test] async fn typename() -> anyhow::Result<()> { - let subgraph_name = SubgraphName::new("typename").unwrap(); - - let hash = { - let test_dir = format!("./integration-tests/{}", subgraph_name); - fixture::build_subgraph(&test_dir).await - }; + let RunnerTestRecipe { + stores, + subgraph_name, + hash, + } = RunnerTestRecipe::new("typename").await; let blocks = { let block_0 = genesis(); @@ -113,7 +137,6 @@ async fn typename() -> anyhow::Result<()> { let stop_block = blocks.last().unwrap().block.ptr(); - let stores = stores("./integration-tests/config.simple.toml").await; let chain = chain(blocks, &stores, None).await; let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, &chain, None, None).await; @@ -124,13 +147,11 @@ async fn typename() -> anyhow::Result<()> { #[tokio::test] async fn file_data_sources() { - let stores = stores("./integration-tests/config.simple.toml").await; - - let subgraph_name = SubgraphName::new("file-data-sources").unwrap(); - let hash = { - let test_dir = format!("./integration-tests/{}", subgraph_name); - fixture::build_subgraph(&test_dir).await - }; + let RunnerTestRecipe { + stores, + subgraph_name, + hash, + } = RunnerTestRecipe::new("file-data-sources").await; let blocks = { let block_0 = genesis(); @@ -281,13 +302,11 @@ async fn file_data_sources() { #[tokio::test] async fn template_static_filters_false_positives() { - let stores = stores("./integration-tests/config.simple.toml").await; - - let subgraph_name = SubgraphName::new("dynamic-data-source").unwrap(); - let hash = { - let test_dir = format!("./integration-tests/{}", subgraph_name); - fixture::build_subgraph(&test_dir).await - }; + let RunnerTestRecipe { + stores, + subgraph_name, + hash, + } = RunnerTestRecipe::new("dynamic-data-source").await; let blocks = { let block_0 = genesis(); @@ -334,12 +353,11 @@ async fn template_static_filters_false_positives() { #[tokio::test] async fn retry_create_ds() { - let stores = stores("./integration-tests/config.simple.toml").await; - let subgraph_name = SubgraphName::new("data-source-revert2").unwrap(); - let hash = { - let test_dir = format!("./integration-tests/{}", subgraph_name); - fixture::build_subgraph(&test_dir).await - }; + let RunnerTestRecipe { + stores, + subgraph_name, + hash, + } = RunnerTestRecipe::new("data-source-revert2").await; let blocks = { let block0 = genesis(); @@ -397,12 +415,11 @@ async fn retry_create_ds() { #[tokio::test] async fn fatal_error() -> anyhow::Result<()> { - let subgraph_name = SubgraphName::new("fatal-error").unwrap(); - - let hash = { - let test_dir = format!("./integration-tests/{}", subgraph_name); - fixture::build_subgraph(&test_dir).await - }; + let RunnerTestRecipe { + stores, + subgraph_name, + hash, + } = RunnerTestRecipe::new("fatal-error").await; let blocks = { let block_0 = genesis(); @@ -414,7 +431,6 @@ async fn fatal_error() -> anyhow::Result<()> { let stop_block = blocks.last().unwrap().block.ptr(); - let stores = stores("./integration-tests/config.simple.toml").await; let chain = chain(blocks, &stores, None).await; let ctx = fixture::setup(subgraph_name.clone(), &hash, &stores, &chain, None, None).await; @@ -436,3 +452,49 @@ async fn fatal_error() -> anyhow::Result<()> { Ok(()) } + +async fn build_subgraph(dir: &str) -> DeploymentHash { + build_subgraph_with_yarn_cmd(dir, "deploy:test").await +} + +async fn build_subgraph_with_yarn_cmd(dir: &str, yarn_cmd: &str) -> DeploymentHash { + // Test that IPFS is up. + IpfsClient::localhost() + .test() + .await + .expect("Could not connect to IPFS, make sure it's running at port 5001"); + + // Make sure dependencies are present. + + run_cmd( + Command::new("yarn") + .arg("install") + .arg("--mutex") + .arg("file:.yarn-mutex") + .current_dir("./runner-tests/"), + ); + + // Run codegen. + run_cmd(Command::new("yarn").arg("codegen").current_dir(&dir)); + + // Run `deploy` for the side effect of uploading to IPFS, the graph node url + // is fake and the actual deploy call is meant to fail. + let deploy_output = run_cmd( + Command::new("yarn") + .arg(yarn_cmd) + .env("IPFS_URI", "http://127.0.0.1:5001") + .env("GRAPH_NODE_ADMIN_URI", "http://localhost:0") + .current_dir(dir), + ); + + // Hack to extract deployment id from `graph deploy` output. + const ID_PREFIX: &str = "Build completed: "; + let mut line = deploy_output + .lines() + .find(|line| line.contains(ID_PREFIX)) + .expect("found no matching line"); + if !line.starts_with(ID_PREFIX) { + line = &line[5..line.len() - 5]; // workaround for colored output + } + DeploymentHash::new(line.trim_start_matches(ID_PREFIX)).unwrap() +} From b4d589a0ac729d1acb451dcd54d2e7ddb5755531 Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Tue, 14 Feb 2023 02:32:39 +0100 Subject: [PATCH 251/253] graph,graphql,store: nested sorting (#3096) --- docs/environment-variables.md | 3 + graph/src/components/store/mod.rs | 22 + graph/src/env/graphql.rs | 6 + graph/src/lib.rs | 9 +- graphql/src/schema/api.rs | 261 +++++++- graphql/src/store/query.rs | 215 +++++- graphql/tests/query.rs | 351 +++++++++- store/postgres/src/relational.rs | 1 + store/postgres/src/relational_queries.rs | 798 ++++++++++++++++++++++- 9 files changed, 1582 insertions(+), 84 deletions(-) diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 56d5bbed183..84d6f078438 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -126,6 +126,9 @@ those. - `GRAPH_GRAPHQL_DISABLE_BOOL_FILTERS`: disables the ability to use AND/OR filters. This is useful if we want to disable filters because of performance reasons. +- `GRAPH_GRAPHQL_DISABLE_CHILD_SORTING`: disables the ability to use child-based + sorting. This is useful if we want to disable child-based sorting because of + performance reasons. - `GRAPH_GRAPHQL_TRACE_TOKEN`: the token to use to enable query tracing for a GraphQL request. If this is set, requests that have a header `X-GraphTraceQuery` set to this value will include a trace of the SQL diff --git a/graph/src/components/store/mod.rs b/graph/src/components/store/mod.rs index d03c0c26e91..c8268846c3b 100644 --- a/graph/src/components/store/mod.rs +++ b/graph/src/components/store/mod.rs @@ -280,6 +280,24 @@ impl EntityFilter { } } +/// Holds the information needed to query a store. +#[derive(Clone, Debug, PartialEq)] +pub struct EntityOrderByChildInfo { + /// The attribute of the child entity that is used to order the results. + pub sort_by_attribute: Attribute, + /// The attribute that is used to join to the parent and child entity. + pub join_attribute: Attribute, + /// If true, the child entity is derived from the parent entity. + pub derived: bool, +} + +/// Holds the information needed to order the results of a query based on nested entities. +#[derive(Clone, Debug, PartialEq)] +pub enum EntityOrderByChild { + Object(EntityOrderByChildInfo, EntityType), + Interface(EntityOrderByChildInfo, Vec), +} + /// The order in which entities should be restored from a store. #[derive(Clone, Debug, PartialEq)] pub enum EntityOrder { @@ -287,6 +305,10 @@ pub enum EntityOrder { Ascending(String, ValueType), /// Order descending by the given attribute. Use `id` as a tie-breaker Descending(String, ValueType), + /// Order ascending by the given attribute of a child entity. Use `id` as a tie-breaker + ChildAscending(EntityOrderByChild), + /// Order descending by the given attribute of a child entity. Use `id` as a tie-breaker + ChildDescending(EntityOrderByChild), /// Order by the `id` of the entities Default, /// Do not order at all. This speeds up queries where we know that diff --git a/graph/src/env/graphql.rs b/graph/src/env/graphql.rs index 98860a43b4c..725d012b0ee 100644 --- a/graph/src/env/graphql.rs +++ b/graph/src/env/graphql.rs @@ -87,6 +87,9 @@ pub struct EnvVarsGraphQl { /// Set by the flag `GRAPH_GRAPHQL_DISABLE_BOOL_FILTERS`. Off by default. /// Disables AND/OR filters pub disable_bool_filters: bool, + /// Set by the flag `GRAPH_GRAPHQL_DISABLE_CHILD_SORTING`. Off by default. + /// Disables child-based sorting + pub disable_child_sorting: bool, /// Set by `GRAPH_GRAPHQL_TRACE_TOKEN`, the token to use to enable query /// tracing for a GraphQL request. If this is set, requests that have a /// header `X-GraphTraceQuery` set to this value will include a trace of @@ -137,6 +140,7 @@ impl From for EnvVarsGraphQl { error_result_size: x.error_result_size.0 .0, max_operations_per_connection: x.max_operations_per_connection, disable_bool_filters: x.disable_bool_filters.0, + disable_child_sorting: x.disable_child_sorting.0, query_trace_token: x.query_trace_token, } } @@ -185,6 +189,8 @@ pub struct InnerGraphQl { max_operations_per_connection: usize, #[envconfig(from = "GRAPH_GRAPHQL_DISABLE_BOOL_FILTERS", default = "false")] pub disable_bool_filters: EnvVarBoolean, + #[envconfig(from = "GRAPH_GRAPHQL_DISABLE_CHILD_SORTING", default = "false")] + pub disable_child_sorting: EnvVarBoolean, #[envconfig(from = "GRAPH_GRAPHQL_TRACE_TOKEN", default = "")] query_trace_token: String, } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 86fa646537b..eb9b3bbb121 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -122,10 +122,11 @@ pub mod prelude { pub use crate::components::store::{ AttributeNames, BlockNumber, CachedEthereumCall, ChainStore, Child, ChildMultiplicity, EntityCache, EntityChange, EntityChangeOperation, EntityCollection, EntityFilter, - EntityLink, EntityModification, EntityOperation, EntityOrder, EntityQuery, EntityRange, - EntityWindow, EthereumCallCache, ParentLink, PartialBlockPtr, PoolWaitStats, QueryStore, - QueryStoreManager, StoreError, StoreEvent, StoreEventStream, StoreEventStreamBox, - SubgraphStore, UnfailOutcome, WindowAttribute, BLOCK_NUMBER_MAX, + EntityLink, EntityModification, EntityOperation, EntityOrder, EntityOrderByChild, + EntityOrderByChildInfo, EntityQuery, EntityRange, EntityWindow, EthereumCallCache, + ParentLink, PartialBlockPtr, PoolWaitStats, QueryStore, QueryStoreManager, StoreError, + StoreEvent, StoreEventStream, StoreEventStreamBox, SubgraphStore, UnfailOutcome, + WindowAttribute, BLOCK_NUMBER_MAX, }; pub use crate::components::subgraph::{ BlockState, DataSourceTemplateInfo, HostMetrics, RuntimeHost, RuntimeHostBuilder, diff --git a/graphql/src/schema/api.rs b/graphql/src/schema/api.rs index 049ceedf231..6794ee8674a 100644 --- a/graphql/src/schema/api.rs +++ b/graphql/src/schema/api.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use graphql_parser::Pos; +use graphql_parser::{schema::TypeDefinition, Pos}; use inflector::Inflector; use lazy_static::lazy_static; @@ -157,16 +157,7 @@ fn add_order_by_type( description: None, name: type_name, directives: vec![], - values: fields - .iter() - .map(|field| &field.name) - .map(|name| EnumValue { - position: Pos::default(), - description: None, - name: name.to_owned(), - directives: vec![], - }) - .collect(), + values: field_enum_values(schema, fields)?, }); let def = Definition::TypeDefinition(typedef); schema.definitions.push(def); @@ -176,6 +167,80 @@ fn add_order_by_type( Ok(()) } +/// Generates enum values for the given set of fields. +fn field_enum_values( + schema: &Document, + fields: &[Field], +) -> Result, APISchemaError> { + let mut enum_values = vec![]; + for field in fields { + enum_values.push(EnumValue { + position: Pos::default(), + description: None, + name: field.name.to_owned(), + directives: vec![], + }); + enum_values.extend(field_enum_values_from_child_entity(schema, field)?); + } + Ok(enum_values) +} + +fn enum_value_from_child_entity_field( + schema: &Document, + parent_field_name: &str, + field: &Field, +) -> Option { + if ast::is_list_or_non_null_list_field(field) || ast::is_entity_type(schema, &field.field_type) + { + // Sorting on lists or entities is not supported. + None + } else { + Some(EnumValue { + position: Pos::default(), + description: None, + name: format!("{}__{}", parent_field_name, field.name), + directives: vec![], + }) + } +} + +fn field_enum_values_from_child_entity( + schema: &Document, + field: &Field, +) -> Result, APISchemaError> { + fn resolve_supported_type_name(field_type: &Type) -> Option<&String> { + match field_type { + Type::NamedType(name) => Some(name), + Type::ListType(_) => None, + Type::NonNullType(of_type) => resolve_supported_type_name(of_type), + } + } + + let type_name = match ENV_VARS.graphql.disable_child_sorting { + true => None, + false => resolve_supported_type_name(&field.field_type), + }; + + Ok(match type_name { + Some(name) => { + let named_type = schema + .get_named_type(name) + .ok_or_else(|| APISchemaError::TypeNotFound(name.clone()))?; + match named_type { + TypeDefinition::Object(ObjectType { fields, .. }) + | TypeDefinition::Interface(InterfaceType { fields, .. }) => fields + .iter() + .filter_map(|f| { + enum_value_from_child_entity_field(schema, field.name.as_str(), f) + }) + .collect(), + _ => vec![], + } + } + None => vec![], + }) +} + /// Adds a `_filter` enum type for the given fields to the schema. fn add_filter_type( schema: &mut Document, @@ -887,6 +952,180 @@ mod tests { assert_eq!(values, ["id", "name"]); } + #[test] + fn api_schema_contains_field_order_by_enum_for_child_entity() { + let input_schema = parse_schema( + r#" + enum FurType { + NONE + FLUFFY + BRISTLY + } + + type Pet { + id: ID! + name: String! + mostHatedBy: [User!]! + mostLovedBy: [User!]! + } + + interface Recipe { + id: ID! + name: String! + author: User! + lovedBy: [User!]! + ingredients: [String!]! + } + + type FoodRecipe implements Recipe { + id: ID! + name: String! + author: User! + ingredients: [String!]! + } + + type DrinkRecipe implements Recipe { + id: ID! + name: String! + author: User! + ingredients: [String!]! + } + + interface Meal { + id: ID! + name: String! + mostHatedBy: [User!]! + mostLovedBy: [User!]! + } + + type Pizza implements Meal { + id: ID! + name: String! + toppings: [String!]! + mostHatedBy: [User!]! + mostLovedBy: [User!]! + } + + type Burger implements Meal { + id: ID! + name: String! + bun: String! + mostHatedBy: [User!]! + mostLovedBy: [User!]! + } + + type User { + id: ID! + name: String! + favoritePetNames: [String!] + pets: [Pet!]! + favoriteFurType: FurType! + favoritePet: Pet! + leastFavoritePet: Pet @derivedFrom(field: "mostHatedBy") + mostFavoritePets: [Pet!] @derivedFrom(field: "mostLovedBy") + favoriteMeal: Meal! + leastFavoriteMeal: Meal @derivedFrom(field: "mostHatedBy") + mostFavoriteMeals: [Meal!] @derivedFrom(field: "mostLovedBy") + recipes: [Recipe!]! @derivedFrom(field: "author") + } + "#, + ) + .expect("Failed to parse input schema"); + let schema = api_schema(&input_schema).expect("Failed to derived API schema"); + + let user_order_by = schema + .get_named_type("User_orderBy") + .expect("User_orderBy type is missing in derived API schema"); + + let enum_type = match user_order_by { + TypeDefinition::Enum(t) => Some(t), + _ => None, + } + .expect("User_orderBy type is not an enum"); + + let values: Vec<&str> = enum_type + .values + .iter() + .map(|value| value.name.as_str()) + .collect(); + + assert_eq!( + values, + [ + "id", + "name", + "favoritePetNames", + "pets", + "favoriteFurType", + "favoritePet", + "favoritePet__id", + "favoritePet__name", + "leastFavoritePet", + "leastFavoritePet__id", + "leastFavoritePet__name", + "mostFavoritePets", + "favoriteMeal", + "favoriteMeal__id", + "favoriteMeal__name", + "leastFavoriteMeal", + "leastFavoriteMeal__id", + "leastFavoriteMeal__name", + "mostFavoriteMeals", + "recipes", + ] + ); + + let meal_order_by = schema + .get_named_type("Meal_orderBy") + .expect("Meal_orderBy type is missing in derived API schema"); + + let enum_type = match meal_order_by { + TypeDefinition::Enum(t) => Some(t), + _ => None, + } + .expect("Meal_orderBy type is not an enum"); + + let values: Vec<&str> = enum_type + .values + .iter() + .map(|value| value.name.as_str()) + .collect(); + + assert_eq!(values, ["id", "name", "mostHatedBy", "mostLovedBy",]); + + let recipe_order_by = schema + .get_named_type("Recipe_orderBy") + .expect("Recipe_orderBy type is missing in derived API schema"); + + let enum_type = match recipe_order_by { + TypeDefinition::Enum(t) => Some(t), + _ => None, + } + .expect("Recipe_orderBy type is not an enum"); + + let values: Vec<&str> = enum_type + .values + .iter() + .map(|value| value.name.as_str()) + .collect(); + + assert_eq!( + values, + [ + "id", + "name", + "author", + "author__id", + "author__name", + "author__favoriteFurType", + "author__favoritePet", + "author__leastFavoritePet", + "lovedBy", + "ingredients" + ] + ); + } + #[test] fn api_schema_contains_object_type_filter_enum() { let input_schema = parse_schema( diff --git a/graphql/src/store/query.rs b/graphql/src/store/query.rs index add79d0d14d..98f25e97dd5 100644 --- a/graphql/src/store/query.rs +++ b/graphql/src/store/query.rs @@ -53,15 +53,73 @@ pub(crate) fn build_query<'a>( query = query.filter(filter); } let order = match ( - build_order_by(entity, field)?, + build_order_by(entity, field, schema)?, build_order_direction(field)?, ) { - (Some((attr, value_type)), OrderDirection::Ascending) => { + (Some((attr, value_type, None)), OrderDirection::Ascending) => { EntityOrder::Ascending(attr, value_type) } - (Some((attr, value_type)), OrderDirection::Descending) => { + (Some((attr, value_type, None)), OrderDirection::Descending) => { EntityOrder::Descending(attr, value_type) } + (Some((attr, _, Some(child))), OrderDirection::Ascending) => { + if ENV_VARS.graphql.disable_child_sorting { + return Err(QueryExecutionError::NotSupported( + "Sorting by child attributes is not supported".to_string(), + )); + } + match child { + OrderByChild::Object(child) => { + EntityOrder::ChildAscending(EntityOrderByChild::Object( + EntityOrderByChildInfo { + sort_by_attribute: attr, + join_attribute: child.join_attribute, + derived: child.derived, + }, + child.entity_type, + )) + } + OrderByChild::Interface(child) => { + EntityOrder::ChildAscending(EntityOrderByChild::Interface( + EntityOrderByChildInfo { + sort_by_attribute: attr, + join_attribute: child.join_attribute, + derived: child.derived, + }, + child.entity_types, + )) + } + } + } + (Some((attr, _, Some(child))), OrderDirection::Descending) => { + if ENV_VARS.graphql.disable_child_sorting { + return Err(QueryExecutionError::NotSupported( + "Sorting by child attributes is not supported".to_string(), + )); + } + match child { + OrderByChild::Object(child) => { + EntityOrder::ChildDescending(EntityOrderByChild::Object( + EntityOrderByChildInfo { + sort_by_attribute: attr, + join_attribute: child.join_attribute, + derived: child.derived, + }, + child.entity_type, + )) + } + OrderByChild::Interface(child) => { + EntityOrder::ChildDescending(EntityOrderByChild::Interface( + EntityOrderByChildInfo { + sort_by_attribute: attr, + join_attribute: child.join_attribute, + derived: child.derived, + }, + child.entity_types, + )) + } + } + } (None, _) => EntityOrder::Default, }; query = query.order(order); @@ -438,27 +496,150 @@ fn list_values(value: Value, filter_type: &str) -> Result, QueryExecu } } +enum OrderByValue { + Direct(String), + Child(String, String), +} + +fn parse_order_by(enum_value: &String) -> Result { + let mut parts = enum_value.split("__"); + let first = parts.next().ok_or_else(|| { + QueryExecutionError::ValueParseError( + "Invalid order value".to_string(), + enum_value.to_string(), + ) + })?; + let second = parts.next(); + + Ok(match second { + Some(second) => OrderByValue::Child(first.to_string(), second.to_string()), + None => OrderByValue::Direct(first.to_string()), + }) +} + +struct ObjectOrderDetails { + entity_type: EntityType, + join_attribute: Attribute, + derived: bool, +} + +struct InterfaceOrderDetails { + entity_types: Vec, + join_attribute: Attribute, + derived: bool, +} + +enum OrderByChild { + Object(ObjectOrderDetails), + Interface(InterfaceOrderDetails), +} + /// Parses GraphQL arguments into an field name to order by, if present. fn build_order_by( entity: ObjectOrInterface, field: &a::Field, -) -> Result, QueryExecutionError> { + schema: &ApiSchema, +) -> Result)>, QueryExecutionError> { match field.argument_value("orderBy") { - Some(r::Value::Enum(name)) => { - let field = sast::get_field(entity, name).ok_or_else(|| { - QueryExecutionError::EntityFieldError(entity.name().to_owned(), name.clone()) - })?; - sast::get_field_value_type(&field.field_type) - .map(|value_type| Some((name.to_owned(), value_type))) - .map_err(|_| { - QueryExecutionError::OrderByNotSupportedError( + Some(r::Value::Enum(name)) => match parse_order_by(name)? { + OrderByValue::Direct(name) => { + let field = sast::get_field(entity, name.as_str()).ok_or_else(|| { + QueryExecutionError::EntityFieldError(entity.name().to_owned(), name.clone()) + })?; + sast::get_field_value_type(&field.field_type) + .map(|value_type| Some((name.to_owned(), value_type, None))) + .map_err(|_| { + QueryExecutionError::OrderByNotSupportedError( + entity.name().to_owned(), + name.clone(), + ) + }) + } + OrderByValue::Child(parent_field_name, child_field_name) => { + if entity.is_interface() { + return Err(QueryExecutionError::OrderByNotSupportedError( entity.name().to_owned(), - name.clone(), - ) - }) - } + parent_field_name.clone(), + )); + } + + let field = + sast::get_field(entity, parent_field_name.as_str()).ok_or_else(|| { + QueryExecutionError::EntityFieldError( + entity.name().to_owned(), + parent_field_name.clone(), + ) + })?; + let derived = field.is_derived(); + let base_type = field.field_type.get_base_type(); + let child_entity = schema + .object_or_interface(base_type) + .ok_or_else(|| QueryExecutionError::NamedTypeError(base_type.into()))?; + let child_field = sast::get_field(child_entity, child_field_name.as_str()) + .ok_or_else(|| { + QueryExecutionError::EntityFieldError( + child_entity.name().to_owned(), + child_field_name.clone(), + ) + })?; + + let join_attribute = match derived { + true => sast::get_derived_from_field(child_entity, field) + .ok_or_else(|| { + QueryExecutionError::EntityFieldError( + entity.name().to_string(), + field.name.to_string(), + ) + })? + .name + .to_string(), + false => parent_field_name, + }; + + let child = match child_entity { + ObjectOrInterface::Object(_) => OrderByChild::Object(ObjectOrderDetails { + entity_type: EntityType::new(base_type.into()), + join_attribute, + derived, + }), + ObjectOrInterface::Interface(interface) => { + let entity_types = schema + .types_for_interface() + .get(&EntityType::new(interface.name.to_string())) + .map(|object_types| { + object_types + .iter() + .map(|object_type| EntityType::new(object_type.name.clone())) + .collect::>() + }) + .ok_or(QueryExecutionError::AbstractTypeError( + "Interface not implemented by any object type".to_string(), + ))?; + OrderByChild::Interface(InterfaceOrderDetails { + entity_types, + join_attribute, + derived, + }) + } + }; + + sast::get_field_value_type(&child_field.field_type) + .map(|value_type| Some((child_field_name.to_owned(), value_type, Some(child)))) + .map_err(|_| { + QueryExecutionError::OrderByNotSupportedError( + child_entity.name().to_owned(), + child_field_name.clone(), + ) + }) + } + }, _ => match field.argument_value("text") { - Some(r::Value::Object(filter)) => build_fulltext_order_by_from_object(filter), + Some(r::Value::Object(filter)) => { + build_fulltext_order_by_from_object(filter).map(|order_by| match order_by { + Some((attr, value)) => Some((attr, value, None)), + None => None, + }) + } None => Ok(None), _ => Err(QueryExecutionError::InvalidFilterError), }, diff --git a/graphql/tests/query.rs b/graphql/tests/query.rs index e906f06ed7a..c4974b9728e 100644 --- a/graphql/tests/query.rs +++ b/graphql/tests/query.rs @@ -190,53 +190,45 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { id: Bytes! } - interface Review { + interface Review @entity { id: ID! body: String! - author: User! + author: Author! } type SongReview implements Review @entity { id: ID! body: String! song: Song - author: User! + author: Author! } type BandReview implements Review @entity { id: ID! body: String! band: Band - author: User! - } - - type User @entity { - id: ID! - name: String! - bandReviews: [BandReview!]! @derivedFrom(field: \"author\") - songReviews: [SongReview!]! @derivedFrom(field: \"author\") - reviews: [Review!]! @derivedFrom(field: \"author\") - latestSongReview: SongReview! - latestBandReview: BandReview! - latestReview: Review! + author: Author! } interface Media { id: ID! title: String! song: Song! + author: User! } type Photo implements Media @entity { id: ID! title: String! song: Song! @derivedFrom(field: \"media\") + author: User! } type Video implements Media @entity { id: ID! title: String! song: Song! @derivedFrom(field: \"media\") + author: User! } interface Release { @@ -258,6 +250,29 @@ fn test_schema(id: DeploymentHash, id_type: IdType) -> Schema { title: String! songs: [Song!]! } + + interface Author { + id: ID! + name: String! + } + + type User implements Author @entity { + id: ID! + name: String! + reviews: [Review!]! @derivedFrom(field: \"author\") + bandReviews: [BandReview!]! @derivedFrom(field: \"author\") + songReviews: [SongReview!]! @derivedFrom(field: \"author\") + latestSongReview: SongReview! + latestBandReview: BandReview! + latestReview: Review! + medias: [Media!]! @derivedFrom(field: \"author\") + } + + type AnonymousUser implements Author @entity { + id: ID! + name: String! + reviews: [Review!]! @derivedFrom(field: \"author\") + } "; Schema::parse(&SCHEMA.replace("@ID@", id_type.as_str()), id).expect("Test schema invalid") @@ -296,21 +311,24 @@ async fn insert_test_entities( entity! { __typename: "Song", id: s[4], sid: "s4", title: "Folk Tune", publisher: "0xb1", writtenBy: "m3", media: vec![md[6]] }, entity! { __typename: "SongStat", id: s[1], played: 10 }, entity! { __typename: "SongStat", id: s[2], played: 15 }, - entity! { __typename: "BandReview", id: "r1", body: "Bad musicians", band: "b1", author: "u1" }, - entity! { __typename: "BandReview", id: "r2", body: "Good amateurs", band: "b2", author: "u2" }, - entity! { __typename: "SongReview", id: "r3", body: "Bad", song: s[2], author: "u1" }, - entity! { __typename: "SongReview", id: "r4", body: "Good", song: s[3], author: "u2" }, - entity! { __typename: "User", id: "u1", name: "Baden", latestSongReview: "r3", latestBandReview: "r1", latestReview: "r1" }, - entity! { __typename: "User", id: "u2", name: "Goodwill", latestSongReview: "r4", latestBandReview: "r2", latestReview: "r2" }, - entity! { __typename: "Photo", id: md[1], title: "Cheesy Tune Single Cover" }, - entity! { __typename: "Video", id: md[2], title: "Cheesy Tune Music Video" }, - entity! { __typename: "Photo", id: md[3], title: "Rock Tune Single Cover" }, - entity! { __typename: "Video", id: md[4], title: "Rock Tune Music Video" }, - entity! { __typename: "Photo", id: md[5], title: "Pop Tune Single Cover" }, - entity! { __typename: "Video", id: md[6], title: "Folk Tune Music Video" }, - entity! { __typename: "Album", id: "rl1", title: "Pop and Folk", songs: vec![s[3], s[4]] }, - entity! { __typename: "Single", id: "rl2", title: "Rock", songs: vec![s[2]] }, - entity! { __typename: "Single", id: "rl3", title: "Cheesy", songs: vec![s[1]] }, + entity! { __typename: "BandReview", id: "r1", body: "Bad musicians", band: "b1", author: "u1" }, + entity! { __typename: "BandReview", id: "r2", body: "Good amateurs", band: "b2", author: "u2" }, + entity! { __typename: "BandReview", id: "r5", body: "Very Bad musicians", band: "b1", author: "u3" }, + entity! { __typename: "SongReview", id: "r3", body: "Bad", song: s[2], author: "u1" }, + entity! { __typename: "SongReview", id: "r4", body: "Good", song: s[3], author: "u2" }, + entity! { __typename: "SongReview", id: "r6", body: "Very Bad", song: s[2], author: "u3" }, + entity! { __typename: "User", id: "u1", name: "Baden", latestSongReview: "r3", latestBandReview: "r1", latestReview: "r1" }, + entity! { __typename: "User", id: "u2", name: "Goodwill", latestSongReview: "r4", latestBandReview: "r2", latestReview: "r2" }, + entity! { __typename: "AnonymousUser", id: "u3", name: "Anonymous 3", latestSongReview: "r6", latestBandReview: "r5", latestReview: "r5" }, + entity! { __typename: "Photo", id: md[1], title: "Cheesy Tune Single Cover", author: "u1" }, + entity! { __typename: "Video", id: md[2], title: "Cheesy Tune Music Video", author: "u2" }, + entity! { __typename: "Photo", id: md[3], title: "Rock Tune Single Cover", author: "u1" }, + entity! { __typename: "Video", id: md[4], title: "Rock Tune Music Video", author: "u2" }, + entity! { __typename: "Photo", id: md[5], title: "Pop Tune Single Cover", author: "u1" }, + entity! { __typename: "Video", id: md[6], title: "Folk Tune Music Video", author: "u2" }, + entity! { __typename: "Album", id: "rl1", title: "Pop and Folk", songs: vec![s[3], s[4]] }, + entity! { __typename: "Single", id: "rl2", title: "Rock", songs: vec![s[2]] }, + entity! { __typename: "Single", id: "rl3", title: "Cheesy", songs: vec![s[1]] }, ]; let entities1 = vec![ @@ -669,6 +687,279 @@ fn can_query_many_to_many_relationship() { }) } +#[test] +fn can_query_with_sorting_by_child_entity() { + const QUERY: &str = " + query { + desc: musicians(first: 100, orderBy: mainBand__name, orderDirection: desc) { + name + mainBand { + name + } + } + asc: musicians(first: 100, orderBy: mainBand__name, orderDirection: asc) { + name + mainBand { + name + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + desc: vec![ + object! { name: "Valerie", mainBand: r::Value::Null }, + object! { name: "Lisa", mainBand: object! { name: "The Musicians" } }, + object! { name: "John", mainBand: object! { name: "The Musicians" } }, + object! { name: "Tom", mainBand: object! { name: "The Amateurs"} }, + ], + asc: vec![ + object! { name: "Tom", mainBand: object! { name: "The Amateurs"} }, + object! { name: "John", mainBand: object! { name: "The Musicians" } }, + object! { name: "Lisa", mainBand: object! { name: "The Musicians" } }, + object! { name: "Valerie", mainBand: r::Value::Null }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_with_sorting_by_derived_child_entity() { + const QUERY: &str = " + query { + desc: songStats(first: 100, orderBy: song__title, orderDirection: desc) { + id + song { + id + title + } + played + } + asc: songStats(first: 100, orderBy: song__title, orderDirection: asc) { + id + song { + id + title + } + played + } + }"; + + run_query(QUERY, |result, id_type| { + let s = id_type.songs(); + let exp = object! { + desc: vec![ + object! { + id: s[2], + song: object! { id: s[2], title: "Rock Tune" }, + played: 15 + }, + object! { + id: s[1], + song: object! { id: s[1], title: "Cheesy Tune" }, + played: 10, + } + ], + asc: vec![ + object! { + id: s[1], + song: object! { id: s[1], title: "Cheesy Tune" }, + played: 10, + }, + object! { + id: s[2], + song: object! { id: s[2], title: "Rock Tune" }, + played: 15 + } + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_with_sorting_by_child_entity_id() { + const QUERY: &str = " + query { + desc: bandReviews(first: 100, orderBy: author__id, orderDirection: desc) { + body + author { + name + } + } + asc: bandReviews(first: 100, orderBy: author__id, orderDirection: asc) { + body + author { + name + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + desc: vec![ + object! { body: "Very Bad musicians", author: object! { name: "Anonymous 3" } }, + object! { body: "Good amateurs", author: object! { name: "Goodwill" } }, + object! { body: "Bad musicians", author: object! { name: "Baden" } }, + ], + asc: vec![ + object! { body: "Bad musicians", author: object! { name: "Baden" } }, + object! { body: "Good amateurs", author: object! { name: "Goodwill" } }, + object! { body: "Very Bad musicians", author: object! { name: "Anonymous 3" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_with_sorting_by_derived_child_entity_id() { + const QUERY: &str = " + query { + desc: songStats(first: 100, orderBy: song__id, orderDirection: desc) { + id + song { + id + title + } + played + } + asc: songStats(first: 100, orderBy: song__id, orderDirection: asc) { + id + song { + id + title + } + played + } + }"; + + run_query(QUERY, |result, id_type| { + let s = id_type.songs(); + let exp = object! { + desc: vec![ + object! { + id: s[2], + song: object! { id: s[2], title: "Rock Tune" }, + played: 15 + }, + object! { + id: s[1], + song: object! { id: s[1], title: "Cheesy Tune" }, + played: 10, + } + ], + asc: vec![ + object! { + id: s[1], + song: object! { id: s[1], title: "Cheesy Tune" }, + played: 10, + }, + object! { + id: s[2], + song: object! { id: s[2], title: "Rock Tune" }, + played: 15 + } + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_query_with_sorting_by_child_interface() { + const QUERY: &str = " + query { + desc: songReviews(first: 100, orderBy: author__name, orderDirection: desc) { + body + author { + name + } + } + asc: songReviews(first: 100, orderBy: author__name, orderDirection: asc) { + body + author { + name + } + } + }"; + + run_query(QUERY, |result, _| { + let exp = object! { + desc: vec![ + object! { body: "Good", author: object! { name: "Goodwill" } }, + object! { body: "Bad", author: object! { name: "Baden" } }, + object! { body: "Very Bad", author: object! { name: "Anonymous 3" } }, + ], + asc: vec![ + object! { body: "Very Bad", author: object! { name: "Anonymous 3" } }, + object! { body: "Bad", author: object! { name: "Baden" } }, + object! { body: "Good", author: object! { name: "Goodwill" } }, + ] + }; + + let data = extract_data!(result).unwrap(); + assert_eq!(data, exp); + }) +} + +#[test] +fn can_not_query_interface_with_sorting_by_child_entity() { + const QUERY: &str = " + query { + desc: medias(first: 100, orderBy: author__name, orderDirection: desc) { + title + author { + name + } + } + asc: medias(first: 100, orderBy: author__name, orderDirection: asc) { + title + author { + name + } + } + }"; + + run_query(QUERY, |result, _| { + // Sorting an interface by child-level entity (derived) is not supported + assert!(result.has_errors()); + }); +} + +#[test] +fn can_not_query_interface_with_sorting_by_derived_child_entity() { + const QUERY: &str = " + query { + desc: medias(first: 100, orderBy: song__title, orderDirection: desc) { + title + song { + title + } + } + asc: medias(first: 100, orderBy: song__title, orderDirection: asc) { + title + song { + title + } + } + }"; + + run_query(QUERY, |result, _| { + // Sorting an interface by child-level entity is not supported + assert!(result.has_errors()); + }); +} + #[test] fn can_query_with_child_filter_on_list_type_field() { const QUERY: &str = " diff --git a/store/postgres/src/relational.rs b/store/postgres/src/relational.rs index 4c038f79669..7c88437918c 100644 --- a/store/postgres/src/relational.rs +++ b/store/postgres/src/relational.rs @@ -687,6 +687,7 @@ impl Layout { FilterCollection::new(self, query.collection, query.filter.as_ref(), query.block)?; let query = FilterQuery::new( &filter_collection, + &self, query.filter.as_ref(), query.order, query.range, diff --git a/store/postgres/src/relational_queries.rs b/store/postgres/src/relational_queries.rs index e311835437b..339dce1db11 100644 --- a/store/postgres/src/relational_queries.rs +++ b/store/postgres/src/relational_queries.rs @@ -17,8 +17,8 @@ use graph::data::value::Word; use graph::data_source::CausalityRegion; use graph::prelude::{ anyhow, r, serde_json, Attribute, BlockNumber, ChildMultiplicity, Entity, EntityCollection, - EntityFilter, EntityLink, EntityOrder, EntityRange, EntityWindow, ParentLink, - QueryExecutionError, StoreError, Value, ENV_VARS, + EntityFilter, EntityLink, EntityOrder, EntityOrderByChild, EntityOrderByChildInfo, EntityRange, + EntityWindow, ParentLink, QueryExecutionError, StoreError, Value, ENV_VARS, }; use graph::{ components::store::{AttributeNames, EntityType}, @@ -2119,7 +2119,7 @@ impl<'a> FilterWindow<'a> { out.push_sql("\n/* children_type_a */ from unnest("); column.bind_ids(&self.ids, out)?; out.push_sql(") as p(id) cross join lateral (select "); - write_column_names(&self.column_names, self.table, out)?; + write_column_names(&self.column_names, self.table, None, out)?; out.push_sql(" from "); out.push_sql(self.table.qualified_name.as_str()); out.push_sql(" c where "); @@ -2198,7 +2198,7 @@ impl<'a> FilterWindow<'a> { out.push_sql("\n/* children_type_b */ from unnest("); column.bind_ids(&self.ids, out)?; out.push_sql(") as p(id) cross join lateral (select "); - write_column_names(&self.column_names, self.table, out)?; + write_column_names(&self.column_names, self.table, None, out)?; out.push_sql(" from "); out.push_sql(self.table.qualified_name.as_str()); out.push_sql(" c where "); @@ -2268,7 +2268,7 @@ impl<'a> FilterWindow<'a> { self.table.primary_key().push_matrix(child_ids, out)?; out.push_sql(")) as p(id, child_ids)"); out.push_sql(" cross join lateral (select "); - write_column_names(&self.column_names, self.table, out)?; + write_column_names(&self.column_names, self.table, None, out)?; out.push_sql(" from "); out.push_sql(self.table.qualified_name.as_str()); out.push_sql(" c where "); @@ -2579,9 +2579,69 @@ impl<'a> FilterCollection<'a> { } } +#[derive(Debug, Clone)] +pub struct ChildKeyDetails<'a> { + /// Table representing the parent entity + pub parent_table: &'a Table, + /// Column in the parent table that stores the connection between the parent and the child + pub parent_join_column: &'a Column, + /// Table representing the child entity + pub child_table: &'a Table, + /// Column in the child table that stores the connection between the child and the parent + pub child_join_column: &'a Column, + /// Column of the child table that sorting is done on + pub sort_by_column: &'a Column, + /// Prefix for the child table + pub prefix: String, + /// Either `asc` or `desc` + pub direction: &'static str, +} + +#[derive(Debug, Clone)] +pub struct ChildKeyAndIdSharedDetails<'a> { + /// Table representing the parent entity + pub parent_table: &'a Table, + /// Column in the parent table that stores the connection between the parent and the child + pub parent_join_column: &'a Column, + /// Table representing the child entity + pub child_table: &'a Table, + /// Column in the child table that stores the connection between the child and the parent + pub child_join_column: &'a Column, + /// Column of the child table that sorting is done on + pub sort_by_column: &'a Column, + /// Prefix for the child table + pub prefix: String, + /// Either `asc` or `desc` + pub direction: &'static str, +} + +#[derive(Debug, Clone)] +pub struct ChildIdDetails<'a> { + /// Table representing the parent entity + pub parent_table: &'a Table, + /// Column in the parent table that stores the connection between the parent and the child + pub parent_join_column: &'a Column, + /// Table representing the child entity + pub child_table: &'a Table, + /// Column in the child table that stores the connection between the child and the parent + pub child_join_column: &'a Column, + /// Prefix for the child table + pub prefix: String, +} + +#[derive(Debug, Clone)] +pub enum ChildKey<'a> { + Single(ChildKeyDetails<'a>), + Many(Vec>), + IdAsc(ChildIdDetails<'a>, Option>), + IdDesc(ChildIdDetails<'a>, Option>), + ManyIdAsc(Vec>, Option>), + ManyIdDesc(Vec>, Option>), +} + /// Convenience to pass the name of the column to order by around. If `name` /// is `None`, the sort key should be ignored -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub enum SortKey<'a> { None, /// Order by `id asc` @@ -2594,20 +2654,22 @@ pub enum SortKey<'a> { value: Option<&'a str>, direction: &'static str, }, + /// Order by some other column; `column` will never be `id` + ChildKey(ChildKey<'a>), } /// String representation that is useful for debugging when `walk_ast` fails impl<'a> fmt::Display for SortKey<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use SortKey::*; - match self { - None => write!(f, "none"), - IdAsc(Option::None) => write!(f, "{}", PRIMARY_KEY_COLUMN), - IdAsc(Some(br)) => write!(f, "{}, {}", PRIMARY_KEY_COLUMN, br.column_name()), - IdDesc(Option::None) => write!(f, "{} desc", PRIMARY_KEY_COLUMN), - IdDesc(Some(br)) => write!(f, "{} desc, {} desc", PRIMARY_KEY_COLUMN, br.column_name()), - Key { + SortKey::None => write!(f, "none"), + SortKey::IdAsc(Option::None) => write!(f, "{}", PRIMARY_KEY_COLUMN), + SortKey::IdAsc(Some(br)) => write!(f, "{}, {}", PRIMARY_KEY_COLUMN, br.column_name()), + SortKey::IdDesc(Option::None) => write!(f, "{} desc", PRIMARY_KEY_COLUMN), + SortKey::IdDesc(Some(br)) => { + write!(f, "{} desc, {} desc", PRIMARY_KEY_COLUMN, br.column_name()) + } + SortKey::Key { column, value: _, direction, @@ -2619,20 +2681,117 @@ impl<'a> fmt::Display for SortKey<'a> { PRIMARY_KEY_COLUMN, direction ), + SortKey::ChildKey(child) => match child { + ChildKey::Single(details) => write!( + f, + "{}.{} {}, {}.{} {}", + details.child_table.name.as_str(), + details.sort_by_column.name.as_str(), + details.direction, + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN, + details.direction + ), + ChildKey::Many(details) => details.iter().try_for_each(|details| { + write!( + f, + "{}.{} {}, {}.{} {}", + details.child_table.name.as_str(), + details.sort_by_column.name.as_str(), + details.direction, + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN, + details.direction + ) + }), + + ChildKey::ManyIdAsc(details, Option::None) => { + details.iter().try_for_each(|details| { + write!( + f, + "{}.{}", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN + ) + }) + } + ChildKey::ManyIdAsc(details, Some(br)) => details.iter().try_for_each(|details| { + write!( + f, + "{}.{}, {}.{}", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN, + details.child_table.name.as_str(), + br.column_name() + ) + }), + ChildKey::ManyIdDesc(details, Option::None) => { + details.iter().try_for_each(|details| { + write!( + f, + "{}.{} desc", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN + ) + }) + } + ChildKey::ManyIdDesc(details, Some(br)) => details.iter().try_for_each(|details| { + write!( + f, + "{}.{} desc, {}.{} desc", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN, + details.child_table.name.as_str(), + br.column_name() + ) + }), + + ChildKey::IdAsc(details, Option::None) => write!( + f, + "{}.{}", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN + ), + ChildKey::IdAsc(details, Some(br)) => write!( + f, + "{}.{}, {}.{}", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN, + details.child_table.name.as_str(), + br.column_name() + ), + ChildKey::IdDesc(details, Option::None) => write!( + f, + "{}.{} desc", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN + ), + ChildKey::IdDesc(details, Some(br)) => { + write!( + f, + "{}.{} desc, {}.{} desc", + details.child_table.name.as_str(), + PRIMARY_KEY_COLUMN, + details.child_table.name.as_str(), + br.column_name() + ) + } + }, } } } +const ASC: &str = "asc"; +const DESC: &str = "desc"; + impl<'a> SortKey<'a> { fn new( order: EntityOrder, collection: &'a FilterCollection, filter: Option<&'a EntityFilter>, block: BlockNumber, + layout: &'a Layout, ) -> Result { - const ASC: &str = "asc"; - const DESC: &str = "desc"; - fn with_key<'a>( table: &'a Table, attribute: String, @@ -2669,6 +2828,223 @@ impl<'a> SortKey<'a> { } } + fn with_child_object_key<'a>( + parent_table: &'a Table, + child_table: &'a Table, + join_attribute: String, + derived: bool, + attribute: String, + br_column: Option>, + direction: &'static str, + ) -> Result, QueryExecutionError> { + let sort_by_column = child_table.column_for_field(&attribute)?; + if sort_by_column.is_fulltext() { + Err(QueryExecutionError::NotSupported( + "Sorting by fulltext fields".to_string(), + )) + } else { + let (parent_column, child_column) = match derived { + true => ( + parent_table.primary_key(), + child_table.column_for_field(&join_attribute).map_err(|_| { + graph::constraint_violation!( + "Column for a join attribute `{}` of `{}` table not found", + join_attribute, + child_table.name.as_str() + ) + })?, + ), + false => ( + parent_table + .column_for_field(&join_attribute) + .map_err(|_| { + graph::constraint_violation!( + "Column for a join attribute `{}` of `{}` table not found", + join_attribute, + parent_table.name.as_str() + ) + })?, + child_table.primary_key(), + ), + }; + + if sort_by_column.is_primary_key() { + return match direction { + ASC => Ok(SortKey::ChildKey(ChildKey::IdAsc( + ChildIdDetails { + parent_table, + child_table, + parent_join_column: parent_column, + child_join_column: child_column, + prefix: "cc".to_string(), + }, + br_column, + ))), + DESC => Ok(SortKey::ChildKey(ChildKey::IdDesc( + ChildIdDetails { + parent_table, + child_table, + parent_join_column: parent_column, + child_join_column: child_column, + prefix: "cc".to_string(), + }, + br_column, + ))), + _ => unreachable!("direction is 'asc' or 'desc'"), + }; + } + + Ok(SortKey::ChildKey(ChildKey::Single(ChildKeyDetails { + parent_table, + child_table, + parent_join_column: parent_column, + child_join_column: child_column, + /// Sort by this column + sort_by_column, + prefix: "cc".to_string(), + direction, + }))) + } + } + + fn build_children_vec<'a>( + layout: &'a Layout, + parent_table: &'a Table, + entity_types: Vec, + child: EntityOrderByChildInfo, + direction: &'static str, + ) -> Result>, QueryExecutionError> { + return entity_types + .iter() + .enumerate() + .map(|(i, entity_type)| { + let child_table = layout.table_for_entity(entity_type)?; + let sort_by_column = child_table.column_for_field(&child.sort_by_attribute)?; + if sort_by_column.is_fulltext() { + Err(QueryExecutionError::NotSupported( + "Sorting by fulltext fields".to_string(), + )) + } else { + let (parent_column, child_column) = match child.derived { + true => ( + parent_table.primary_key(), + child_table + .column_for_field(&child.join_attribute) + .map_err(|_| { + graph::constraint_violation!( + "Column for a join attribute `{}` of `{}` table not found", + child.join_attribute, + child_table.name.as_str() + ) + })?, + ), + false => ( + parent_table + .column_for_field(&child.join_attribute) + .map_err(|_| { + graph::constraint_violation!( + "Column for a join attribute `{}` of `{}` table not found", + child.join_attribute, + parent_table.name.as_str() + ) + })?, + child_table.primary_key(), + ), + }; + + Ok(ChildKeyAndIdSharedDetails { + parent_table, + child_table, + parent_join_column: parent_column, + child_join_column: child_column, + prefix: format!("cc{}", i), + sort_by_column, + direction, + }) + } + }) + .collect::>, QueryExecutionError>>(); + } + + fn with_child_interface_key<'a>( + layout: &'a Layout, + parent_table: &'a Table, + child: EntityOrderByChildInfo, + entity_types: Vec, + br_column: Option>, + direction: &'static str, + ) -> Result, QueryExecutionError> { + if let Some(first_entity) = entity_types.first() { + let child_table = layout.table_for_entity(first_entity)?; + let sort_by_column = child_table.column_for_field(&child.sort_by_attribute)?; + + if sort_by_column.is_fulltext() { + Err(QueryExecutionError::NotSupported( + "Sorting by fulltext fields".to_string(), + )) + } else if sort_by_column.is_primary_key() { + if direction == ASC { + Ok(SortKey::ChildKey(ChildKey::ManyIdAsc( + build_children_vec( + layout, + parent_table, + entity_types, + child, + direction, + )? + .iter() + .map(|asd| ChildIdDetails { + parent_table: asd.parent_table, + child_table: asd.child_table, + parent_join_column: asd.parent_join_column, + child_join_column: asd.child_join_column, + prefix: asd.prefix.clone(), + }) + .collect(), + br_column, + ))) + } else { + Ok(SortKey::ChildKey(ChildKey::ManyIdDesc( + build_children_vec( + layout, + parent_table, + entity_types, + child, + direction, + )? + .iter() + .map(|asd| ChildIdDetails { + parent_table: asd.parent_table, + child_table: asd.child_table, + parent_join_column: asd.parent_join_column, + child_join_column: asd.child_join_column, + prefix: asd.prefix.clone(), + }) + .collect(), + br_column, + ))) + } + } else { + Ok(SortKey::ChildKey(ChildKey::Many( + build_children_vec(layout, parent_table, entity_types, child, direction)? + .iter() + .map(|asd| ChildKeyDetails { + parent_table: asd.parent_table, + parent_join_column: asd.parent_join_column, + child_table: asd.child_table, + child_join_column: asd.child_join_column, + sort_by_column: asd.sort_by_column, + prefix: asd.prefix.clone(), + direction: asd.direction.clone(), + }) + .collect(), + ))) + } + } else { + Ok(SortKey::ChildKey(ChildKey::Many(vec![]))) + } + } + // If there is more than one table, we are querying an interface, // and the order is on an attribute in that interface so that all // tables have a column for that. It is therefore enough to just @@ -2688,6 +3064,34 @@ impl<'a> SortKey<'a> { EntityOrder::Descending(attr, _) => with_key(table, attr, filter, DESC, br_column), EntityOrder::Default => Ok(SortKey::IdAsc(br_column)), EntityOrder::Unordered => Ok(SortKey::None), + EntityOrder::ChildAscending(kind) => match kind { + EntityOrderByChild::Object(child, entity_type) => with_child_object_key( + table, + layout.table_for_entity(&entity_type)?, + child.join_attribute, + child.derived, + child.sort_by_attribute, + br_column, + ASC, + ), + EntityOrderByChild::Interface(child, entity_types) => { + with_child_interface_key(layout, table, child, entity_types, br_column, ASC) + } + }, + EntityOrder::ChildDescending(kind) => match kind { + EntityOrderByChild::Object(child, entity_type) => with_child_object_key( + table, + layout.table_for_entity(&entity_type)?, + child.join_attribute, + child.derived, + child.sort_by_attribute, + br_column, + DESC, + ), + EntityOrderByChild::Interface(child, entity_types) => { + with_child_interface_key(layout, table, child, entity_types, br_column, DESC) + } + }, } } @@ -2714,6 +3118,51 @@ impl<'a> SortKey<'a> { out.push_identifier(column.name.as_str())?; Ok(()) } + SortKey::ChildKey(nested) => { + match nested { + ChildKey::Single(child) => { + if child.sort_by_column.is_primary_key() { + return Err(constraint_violation!("SortKey::Key never uses 'id'")); + } + out.push_sql(", "); + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + out.push_identifier(child.sort_by_column.name.as_str())?; + } + ChildKey::Many(children) => { + for child in children.iter() { + if child.sort_by_column.is_primary_key() { + return Err(constraint_violation!("SortKey::Key never uses 'id'")); + } + out.push_sql(", "); + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + out.push_identifier(child.sort_by_column.name.as_str())?; + } + } + ChildKey::ManyIdAsc(children, br_column) + | ChildKey::ManyIdDesc(children, br_column) => { + for child in children.iter() { + if let Some(br_column) = br_column { + out.push_sql(", "); + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + br_column.name(out); + } + } + } + ChildKey::IdAsc(child, br_column) | ChildKey::IdDesc(child, br_column) => { + if let Some(br_column) = br_column { + out.push_sql(", "); + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + br_column.name(out); + } + } + } + + Ok(()) + } } } @@ -2748,7 +3197,70 @@ impl<'a> SortKey<'a> { direction, } => { out.push_sql("order by "); - SortKey::sort_expr(column, value, direction, out) + SortKey::sort_expr(column, value, direction, None, None, out) + } + SortKey::ChildKey(child) => { + out.push_sql("order by "); + match child { + ChildKey::Single(child) => SortKey::sort_expr( + child.sort_by_column, + &None, + child.direction, + Some(&child.prefix), + Some("c"), + out, + ), + ChildKey::Many(children) => { + let columns: Vec<(&Column, &str)> = children + .iter() + .map(|child| (child.sort_by_column, child.prefix.as_str())) + .collect(); + SortKey::multi_sort_expr( + columns, + children.first().unwrap().direction, + Some("c"), + out, + ) + } + + ChildKey::ManyIdAsc(children, br_column) => { + let prefixes: Vec<&str> = + children.iter().map(|child| child.prefix.as_str()).collect(); + SortKey::multi_sort_id_expr(prefixes, ASC, br_column, out) + } + ChildKey::ManyIdDesc(children, br_column) => { + let prefixes: Vec<&str> = + children.iter().map(|child| child.prefix.as_str()).collect(); + SortKey::multi_sort_id_expr(prefixes, DESC, br_column, out) + } + + ChildKey::IdAsc(child, br_column) => { + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + out.push_identifier(PRIMARY_KEY_COLUMN)?; + if let Some(br_column) = br_column { + out.push_sql(", "); + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + br_column.bare_name(out); + } + Ok(()) + } + ChildKey::IdDesc(child, br_column) => { + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + out.push_identifier(PRIMARY_KEY_COLUMN)?; + out.push_sql(" desc"); + if let Some(br_column) = br_column { + out.push_sql(", "); + out.push_sql(child.prefix.as_str()); + out.push_sql("."); + br_column.bare_name(out); + out.push_sql(" desc"); + } + Ok(()) + } + } } } } @@ -2774,7 +3286,12 @@ impl<'a> SortKey<'a> { direction, } => { out.push_sql("order by g$parent_id, "); - SortKey::sort_expr(column, value, direction, out) + SortKey::sort_expr(column, value, direction, None, None, out) + } + SortKey::ChildKey(_) => { + return Err(diesel::result::Error::QueryBuilderError( + "SortKey::ChildKey cannot be used for parent ordering (yet)".into(), + )); } } } @@ -2785,6 +3302,8 @@ impl<'a> SortKey<'a> { column: &Column, value: &Option<&str>, direction: &str, + column_prefix: Option<&str>, + rest_prefix: Option<&str>, out: &mut AstPass, ) -> QueryResult<()> { if column.is_primary_key() { @@ -2794,6 +3313,13 @@ impl<'a> SortKey<'a> { )); } + fn push_prefix(prefix: Option<&str>, out: &mut AstPass) { + if let Some(prefix) = prefix { + out.push_sql(prefix); + out.push_sql("."); + } + } + match &column.column_type { ColumnType::TSVector(config) => { let algorithm = match config.algorithm { @@ -2802,6 +3328,7 @@ impl<'a> SortKey<'a> { }; out.push_sql(algorithm); let name = column.name.as_str(); + push_prefix(column_prefix, out); out.push_identifier(name)?; out.push_sql(", to_tsquery("); @@ -2810,6 +3337,7 @@ impl<'a> SortKey<'a> { } _ => { let name = column.name.as_str(); + push_prefix(column_prefix, out); out.push_identifier(name)?; } } @@ -2819,17 +3347,232 @@ impl<'a> SortKey<'a> { out.push_sql(direction); out.push_sql(" nulls last"); out.push_sql(", "); + push_prefix(rest_prefix, out); out.push_identifier(PRIMARY_KEY_COLUMN)?; } else { out.push_sql(" "); out.push_sql(direction); out.push_sql(", "); + push_prefix(rest_prefix, out); out.push_identifier(PRIMARY_KEY_COLUMN)?; out.push_sql(" "); out.push_sql(direction); } Ok(()) } + + /// Generate + /// [COALESCE(name1, name2) direction,] id1, id2 + fn multi_sort_expr( + columns: Vec<(&Column, &str)>, + direction: &str, + rest_prefix: Option<&str>, + out: &mut AstPass, + ) -> QueryResult<()> { + for (column, _) in columns.iter() { + if column.is_primary_key() { + // This shouldn't happen since we'd use SortKey::ManyIdAsc/ManyDesc + return Err(constraint_violation!( + "multi_sort_expr called with primary key column" + )); + } + + match column.column_type { + ColumnType::TSVector(_) => { + return Err(constraint_violation!("TSVector is not supported")); + } + _ => {} + } + } + + fn push_prefix(prefix: Option<&str>, out: &mut AstPass) { + if let Some(prefix) = prefix { + out.push_sql(prefix); + out.push_sql("."); + } + } + + out.push_sql("coalesce("); + + for (i, (column, prefix)) in columns.iter().enumerate() { + if i != 0 { + out.push_sql(", "); + } + + let name = column.name.as_str(); + push_prefix(Some(prefix), out); + out.push_identifier(name)?; + } + + out.push_sql(") "); + + if ENV_VARS.store.reversible_order_by_off { + // Old behavior + out.push_sql(direction); + out.push_sql(" nulls last"); + out.push_sql(", "); + push_prefix(rest_prefix, out); + out.push_identifier(PRIMARY_KEY_COLUMN)?; + } else { + out.push_sql(direction); + out.push_sql(", "); + push_prefix(rest_prefix, out); + out.push_identifier(PRIMARY_KEY_COLUMN)?; + out.push_sql(" "); + out.push_sql(direction); + } + Ok(()) + } + + /// Generate + /// COALESCE(id1, id2) direction, [COALESCE(br_column1, br_column2) direction] + fn multi_sort_id_expr( + prefixes: Vec<&str>, + direction: &str, + br_column: &Option, + out: &mut AstPass, + ) -> QueryResult<()> { + fn push_prefix(prefix: Option<&str>, out: &mut AstPass) { + if let Some(prefix) = prefix { + out.push_sql(prefix); + out.push_sql("."); + } + } + + out.push_sql("coalesce("); + for (i, prefix) in prefixes.iter().enumerate() { + if i != 0 { + out.push_sql(", "); + } + + push_prefix(Some(prefix), out); + out.push_identifier(PRIMARY_KEY_COLUMN)?; + } + out.push_sql(") "); + + out.push_sql(direction); + + if let Some(br_column) = br_column { + out.push_sql(", coalesce("); + for (i, prefix) in prefixes.iter().enumerate() { + if i != 0 { + out.push_sql(", "); + } + + push_prefix(Some(prefix), out); + br_column.bare_name(out); + } + out.push_sql(") "); + out.push_sql(direction); + } + + Ok(()) + } + + fn add_child(&self, block: BlockNumber, out: &mut AstPass) -> QueryResult<()> { + fn add( + block: BlockNumber, + child_table: &Table, + child_column: &Column, + parent_column: &Column, + prefix: &str, + out: &mut AstPass, + ) -> QueryResult<()> { + out.push_sql(" left join "); + out.push_sql(child_table.qualified_name.as_str()); + out.push_sql(" as "); + out.push_sql(prefix); + out.push_sql(" on ("); + + if child_column.is_list() { + // Type C: p.id = any(c.child_ids) + out.push_sql("c."); + out.push_identifier(parent_column.name.as_str())?; + out.push_sql(" = any("); + out.push_sql(prefix); + out.push_sql("."); + out.push_identifier(child_column.name.as_str())?; + out.push_sql(")"); + } else if parent_column.is_list() { + // Type A: c.id = any(p.{parent_field}) + out.push_sql(prefix); + out.push_sql("."); + out.push_identifier(child_column.name.as_str())?; + out.push_sql(" = any(c."); + out.push_identifier(parent_column.name.as_str())?; + out.push_sql(")"); + } else { + // Type B: c.id = p.{parent_field} + out.push_sql(prefix); + out.push_sql("."); + out.push_identifier(child_column.name.as_str())?; + out.push_sql(" = "); + out.push_sql("c."); + out.push_identifier(parent_column.name.as_str())?; + } + + out.push_sql(" and "); + out.push_sql(prefix); + out.push_sql("."); + out.push_identifier(BLOCK_RANGE_COLUMN)?; + out.push_sql(" @> "); + out.push_bind_param::(&block)?; + out.push_sql(") "); + + Ok(()) + } + + match self { + SortKey::ChildKey(nested) => match nested { + ChildKey::Single(child) => { + add( + block, + &child.child_table, + &child.child_join_column, + &child.parent_join_column, + &child.prefix, + out, + )?; + } + ChildKey::Many(children) => { + for child in children.iter() { + add( + block, + &child.child_table, + &child.child_join_column, + &child.parent_join_column, + &child.prefix, + out, + )?; + } + } + ChildKey::ManyIdAsc(children, _) | ChildKey::ManyIdDesc(children, _) => { + for child in children.iter() { + add( + block, + &child.child_table, + &child.child_join_column, + &child.parent_join_column, + &child.prefix, + out, + )?; + } + } + ChildKey::IdAsc(child, _) | ChildKey::IdDesc(child, _) => { + add( + block, + &child.child_table, + &child.child_join_column, + &child.parent_join_column, + &child.prefix, + out, + )?; + } + }, + _ => {} + } + Ok(()) + } } /// Generate `[limit {first}] [offset {skip}] @@ -2899,6 +3642,7 @@ impl<'a> fmt::Display for FilterQuery<'a> { impl<'a> FilterQuery<'a> { pub fn new( collection: &'a FilterCollection, + layout: &'a Layout, filter: Option<&'a EntityFilter>, order: EntityOrder, range: EntityRange, @@ -2906,7 +3650,7 @@ impl<'a> FilterQuery<'a> { query_id: Option, site: &'a Site, ) -> Result { - let sort_key = SortKey::new(order, collection, filter, block)?; + let sort_key = SortKey::new(order, collection, filter, block, layout)?; Ok(FilterQuery { collection, @@ -2934,6 +3678,8 @@ impl<'a> FilterQuery<'a> { out.push_sql(table.qualified_name.as_str()); out.push_sql(" c"); + self.sort_key.add_child(self.block, &mut out)?; + out.push_sql("\n where "); BlockRangeColumn::new(&table, "c.", self.block).contains(&mut out)?; if let Some(filter) = table_filter { @@ -2971,7 +3717,7 @@ impl<'a> FilterQuery<'a> { ) -> QueryResult<()> { Self::select_entity_and_data(table, &mut out); out.push_sql(" from (select "); - write_column_names(column_names, table, &mut out)?; + write_column_names(column_names, table, Some("c."), &mut out)?; self.filtered_rows(table, filter, out.reborrow())?; out.push_sql("\n "); self.sort_key.order_by(&mut out)?; @@ -3626,13 +4372,21 @@ pub struct CopyVid { fn write_column_names( column_names: &AttributeNames, table: &Table, + prefix: Option<&str>, out: &mut AstPass, ) -> QueryResult<()> { + let prefix = prefix.unwrap_or(""); + match column_names { - AttributeNames::All => out.push_sql(" * "), + AttributeNames::All => { + out.push_sql(" "); + out.push_sql(prefix); + out.push_sql("*"); + } AttributeNames::Select(column_names) => { let mut iterator = iter_column_names(column_names, table, true).peekable(); while let Some(column_name) = iterator.next() { + out.push_sql(prefix); out.push_identifier(column_name)?; if iterator.peek().is_some() { out.push_sql(", "); From 4b5ea166e777a4c892f3971a5a712f01d8a28f89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 13:21:51 +0100 Subject: [PATCH 252/253] build(deps): bump uuid from 0.8.2 to 1.2.2 (#4366) Bumps [uuid](https://github.com/uuid-rs/uuid) from 0.8.2 to 1.2.2. - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](https://github.com/uuid-rs/uuid/compare/0.8.2...1.2.2) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 19 +++++-------------- core/Cargo.toml | 2 +- runtime/wasm/Cargo.toml | 2 +- server/websocket/Cargo.toml | 2 +- store/postgres/Cargo.toml | 2 +- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 791e9dd5391..690bbc33723 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1733,7 +1733,7 @@ dependencies = [ "test-store", "tower 0.4.12", "tower-test", - "uuid 0.8.2", + "uuid", ] [[package]] @@ -1850,7 +1850,7 @@ dependencies = [ "semver", "strum", "strum_macros", - "uuid 1.2.2", + "uuid", "wasm-instrument", "wasmtime", ] @@ -1919,7 +1919,7 @@ dependencies = [ "serde", "serde_derive", "tokio-tungstenite", - "uuid 0.8.2", + "uuid", ] [[package]] @@ -1960,7 +1960,7 @@ dependencies = [ "serde", "stable-hash 0.3.3", "test-store", - "uuid 1.2.2", + "uuid", ] [[package]] @@ -1991,7 +1991,7 @@ dependencies = [ "slog", "tokio", "tokio-stream", - "uuid 1.2.2", + "uuid", ] [[package]] @@ -5060,15 +5060,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", -] - [[package]] name = "uuid" version = "1.2.2" diff --git a/core/Cargo.toml b/core/Cargo.toml index 053b36593b9..21a71303542 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -40,4 +40,4 @@ pretty_assertions = "1.3.0" anyhow = "1.0" ipfs-api-backend-hyper = "0.6" ipfs-api = { version = "0.17.0", features = ["with-hyper-rustls"], default-features = false } -uuid = { version = "0.8.1", features = ["v4"] } +uuid = { version = "1.2.2", features = ["v4"] } diff --git a/runtime/wasm/Cargo.toml b/runtime/wasm/Cargo.toml index dc54850702c..9393385a452 100644 --- a/runtime/wasm/Cargo.toml +++ b/runtime/wasm/Cargo.toml @@ -14,7 +14,7 @@ bs58 = "0.4.0" graph-runtime-derive = { path = "../derive" } semver = "1.0.16" lazy_static = "1.4" -uuid = { version = "1.1.2", features = ["v4"] } +uuid = { version = "1.2.2", features = ["v4"] } strum = "0.21.0" strum_macros = "0.21.1" bytes = "1.0" diff --git a/server/websocket/Cargo.toml b/server/websocket/Cargo.toml index c0d398e54c1..02466d2825b 100644 --- a/server/websocket/Cargo.toml +++ b/server/websocket/Cargo.toml @@ -12,5 +12,5 @@ lazy_static = "1.2.0" serde = "1.0" serde_derive = "1.0" tokio-tungstenite = "0.17" -uuid = { version = "0.8.1", features = ["v4"] } +uuid = { version = "1.2.2", features = ["v4"] } anyhow = "1.0" diff --git a/store/postgres/Cargo.toml b/store/postgres/Cargo.toml index caa1cf36bbb..59e9b1580fb 100644 --- a/store/postgres/Cargo.toml +++ b/store/postgres/Cargo.toml @@ -25,7 +25,7 @@ openssl = "0.10.45" postgres-openssl = "0.5.0" rand = "0.8.4" serde = "1.0" -uuid = { version = "1.1.2", features = ["v4"] } +uuid = { version = "1.2.2", features = ["v4"] } stable-hash_legacy = { version = "0.3.3", package = "stable-hash" } diesel_derives = "1.4.1" anyhow = "1.0.69" From bd909464ce25cd90167e854e44b0aaa80cec6ef4 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Mon, 13 Feb 2023 14:36:39 -0800 Subject: [PATCH 253/253] store: Do not intercept error from layout.delete The old behavior would throw away all the error details which contained much more useful information than the error that was actually returned. --- store/postgres/src/deployment_store.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index ec73672c7ad..45b2b9aaef7 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -416,9 +416,7 @@ impl DeploymentStore { stopwatch: &StopwatchMetrics, ) -> Result { let _section = stopwatch.start_section("apply_entity_modifications_delete"); - layout - .delete(conn, entity_type, entity_keys, block_number(ptr), stopwatch) - .map_err(|_error| anyhow!("Failed to remove entities: {:?}", entity_keys).into()) + layout.delete(conn, entity_type, entity_keys, block_number(ptr), stopwatch) } /// Execute a closure with a connection to the database.