-
Notifications
You must be signed in to change notification settings - Fork 125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: EVM compatible proof plan serialization #432
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm are you sure we don't want to use an existing serialization library such as ethabi, rlp or ssz for this?
TooManyResults, | ||
#[snafu(display("Too many tables"))] | ||
/// The number of tables in a plan is too large | ||
TooManyTables, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should put the exact limit in the error message or people won't even know how many are too many
TooManyTables, | ||
#[snafu(display("Too many columns"))] | ||
/// The number of columns in a plan is too large | ||
TooManyColumns, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same. It's actually plausible to have 256 columns in web2
I originally tried the abi, but found that this format is magnitudes cheaper when actually passing over the AST. Although, I'm now considering changing this to postorder rather than preorder. The idea is that the data is in the exact order that we need it as we do the verification. Saves arithmetic and having to jump around. I'm not sure if it would work as well, though. Not too familiar with rlp or ssz. I don't think they are Solidity/EVM formats, but are used by Ethereum for other things. |
235e2df
to
37d2041
Compare
37d2041
to
9be37bd
Compare
fn we_can_generate_serialized_proof_plan_for_simple_filter() { | ||
let table_ref = "namespace.table".parse().unwrap(); | ||
let identifier_a = "a".parse().unwrap(); | ||
let identifier_b = "b".parse().unwrap(); | ||
let identifier_alias = "alias".parse().unwrap(); | ||
|
||
let column_ref_a = ColumnRef::new(table_ref, identifier_a, ColumnType::BigInt); | ||
let column_ref_b = ColumnRef::new(table_ref, identifier_b, ColumnType::BigInt); | ||
|
||
let plan = DynProofPlan::Filter(FilterExec::new( | ||
vec![AliasedDynProofExpr { | ||
expr: DynProofExpr::Column(ColumnExpr::new(column_ref_b)), | ||
alias: identifier_alias, | ||
}], | ||
TableExpr { table_ref }, | ||
DynProofExpr::Equals(EqualsExpr::new( | ||
Box::new(DynProofExpr::Column(ColumnExpr::new(column_ref_a))), | ||
Box::new(DynProofExpr::Literal(LiteralExpr::new( | ||
LiteralValue::BigInt(5), | ||
))), | ||
)), | ||
)); | ||
|
||
let query_expr = QueryExpr::new(plan, vec![]); | ||
let bytes = serialize_query_expr::<TestScalar>(&query_expr).unwrap(); | ||
let expected_bytes = iter::empty::<u8>() | ||
.chain([FILTER_EXEC_NUM, 0, 1]) // filter expr, table number, result count | ||
.chain([COLUMN_EXPR_NUM, 0]) // column expr, column a (#0) | ||
.chain([EQUALS_EXPR_NUM]) // equals expr | ||
.chain([COLUMN_EXPR_NUM, 1]) // column expr, column b (#1) | ||
.chain([LITERAL_EXPR_NUM, BIGINT_TYPE_NUM]) // literal expr, literal type | ||
.chain([0; 31]) // leading 0s of literal value | ||
.chain([5]) // literal value | ||
.collect_vec(); | ||
assert_eq!(bytes, expected_bytes); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the "integration test": the top level example of what is happening in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could be a basic question but I understand we expect the filter expression to be where a = 5
so we do we have .chain([COLUMN_EXPR_NUM, 1]) // column expr, column b (#1)
in line 151 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let expected_bytes = iter::empty::<u8>()
.chain([FILTER_EXEC_NUM, 0, 1]) // filter expr, table number, result count
.chain([COLUMN_EXPR_NUM, 0]) // column expr, column a (#0)
.chain([EQUALS_EXPR_NUM]) // equals expr
.chain([LITERAL_EXPR_NUM, BIGINT_TYPE_NUM]) // literal expr, literal type
.chain([0; 31]) // leading 0s of literal value
.chain([5]) // literal value
.collect_vec();
@JayWhite2357 what would that be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could be a basic question but I understand we expect the filter expression to be
where a = 5
so we do we have.chain([COLUMN_EXPR_NUM, 1]) // column expr, column b (#1)
in line 151 ?
I flipped the a and b in the comments. It should be the other way. I'll fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let expected_bytes = iter::empty::<u8>() .chain([FILTER_EXEC_NUM, 0, 1]) // filter expr, table number, result count .chain([COLUMN_EXPR_NUM, 0]) // column expr, column a (#0) .chain([EQUALS_EXPR_NUM]) // equals expr .chain([LITERAL_EXPR_NUM, BIGINT_TYPE_NUM]) // literal expr, literal type .chain([0; 31]) // leading 0s of literal value .chain([5]) // literal value .collect_vec(); @JayWhite2357 what would that be?
This is invalid. Would be something like SELECT a FROM table WHERE 5 =
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so the serialized query expression is agnostic to column names. right? I see the query plan create incremental reference to column by their mentioning order
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JayWhite2357 how about result count
does that refer to rows count? and needed for verification? because I don't see it needed within AST
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That refers to the number of results in the filter.
e.g., SELECT a,b,c,d,e,f,g FROM table where a=5
would have 7 as the result count.
#[test] | ||
fn we_can_generate_serialized_proof_plan_for_query_expr() { | ||
let table_ref = "namespace.table".parse().unwrap(); | ||
let identifier_alias = "alias".parse().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is identifier_alias
used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing at the moment. Eventually, it will need to be used to validate the result names, if desired. But, it isn't going to be used by the EVM proof verifier.
e280155
🎉 This PR is included in version 0.62.3 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Rationale for this change
We need Solidity to be able to read Proof Plans.
What changes are included in this PR?
An initial implementation of serialization for Proof Plan is added.
Are these changes tested?
Yes