Skip to content

Commit

Permalink
decent progress on request parse
Browse files Browse the repository at this point in the history
  • Loading branch information
Autoparallel committed Aug 29, 2024
1 parent 0bbd8d7 commit f01c685
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 41 deletions.
4 changes: 2 additions & 2 deletions circuits.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@
]
},
"get_request": {
"file": "parser_http_request/parser",
"file": "http/parser/parser",
"template": "Parser",
"params": [158]
"params": [60]
}
}
5 changes: 3 additions & 2 deletions circuits/http/interpreter.circom
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ include "../utils/array.circom";
/* TODO:
Notes --
- This is a pretty efficient way to simply check what the method used in a request is by checking
the first `DATA_LENGTH` number of bytes.
- Certainly this could be more modular.
the first `DATA_LENGTH` number of bytes.
- Could probably change this to a template that checks if it is one of the given methods
so we don't check them all in one
*/
template YieldMethod(DATA_LENGTH) {
signal input bytes[DATA_LENGTH];
Expand Down
2 changes: 1 addition & 1 deletion circuits/http/parser/language.circom
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ template Syntax() {
signal output QUOTE <== 34;
//-White_space--------------------------------------------------------------------------------//
// - ASCII pair: `\r\n`
signal output CLRF <== [13, 10]; // https://www.rfc-editor.org/rfc/rfc2616#section-2.2
signal output CLRF[2] <== [13, 10]; // https://www.rfc-editor.org/rfc/rfc2616#section-2.2
// https://www.rfc-editor.org/rfc/rfc7230#section-3.5
// - ASCII char: ` `
signal output SPACE <== 32;
Expand Down
71 changes: 47 additions & 24 deletions circuits/http/parser/machine.circom
Original file line number Diff line number Diff line change
@@ -1,28 +1,51 @@
pragma circom 2.1.9;

include "language.circom";
include "../utils/array.circom";

template ParseMethod() {
signal input bytes[7];
signal output MethodTag;

component RequestMethod = RequestMethod();
component RequestMethodTag = RequestMethodTag();

component IsGet = IsEqualArray(3);
for(var byte_idx = 0; byte_idx < 3; byte_idx++) {
IsGet.in[0][byte_idx] <== bytes[byte_idx];
IsGet.in[1][byte_idx] <== RequestMethod.GET[byte_idx];
}
signal TagGet <== IsGet.out * RequestMethodTag.GET;

component IsPost = IsEqualArray(4);
for(var byte_idx = 0; byte_idx < 4; byte_idx++) {
IsPost.in[0][byte_idx] <== bytes[byte_idx];
IsPost.in[1][byte_idx] <== RequestMethod.POST[byte_idx];
}
signal TagPost <== IsPost.out * RequestMethodTag.POST;

MethodTag <== TagGet + TagPost;
include "../../utils/array.circom";

template StateUpdate() {
signal input parsing_start; // Bool flag for if we are in the start line
signal input parsing_header; // Flag + Counter for what header line we are in
signal input parsing_body;
signal input read_clrf; // Bool flag to say whether we just read a CLRF
signal input byte_pair[2];

signal output next_parsing_start;
signal output next_parsing_header;
signal output next_parsing_body;
signal output next_read_clrf;

signal state[3] <== [parsing_start, parsing_header, parsing_body];
component stateToMask = StateToMask();
stateToMask.state <== state;

component Syntax = Syntax();

component pairIsCLRF = IsEqualArray(2);
pairIsCLRF.in <== [byte_pair, Syntax.CLRF];
log("pairIsCLRF: ", pairIsCLRF.out);

component stateChange = ScalarArrayMul(3);
stateChange.array <== stateToMask.mask;
stateChange.scalar <== pairIsCLRF.out;
log("stateChange[0]: ", stateChange.out[0]);
log("stateChange[1]: ", stateChange.out[1]);
log("stateChange[2]: ", stateChange.out[2]);

component nextState = ArrayAdd(3);
nextState.lhs <== state;
nextState.rhs <== stateChange.out;

next_parsing_start <== nextState.out[0];
next_parsing_header <== nextState.out[1];
next_parsing_body <== nextState.out[2];
next_read_clrf <== pairIsCLRF.out;

}

template StateToMask() {
signal input state[3];
signal output mask[3];

mask <== [- state[0], state[0] - state[1], state[2]];
}
40 changes: 35 additions & 5 deletions circuits/http/parser/parser.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma circom 2.1.9;

include "../utils/bytes.circom";
include "../../utils/bytes.circom";
include "machine.circom";


Expand All @@ -16,9 +16,39 @@ template Parser(DATA_BYTES) {
dataASCII.in <== data;
//--------------------------------------------------------------------------------------------//

component ParseMethod = ParseMethod();
for(var byte_idx = 0; byte_idx < 7; byte_idx++) {
ParseMethod.bytes[byte_idx] <== data[byte_idx];
// Initialze the parser
component State[DATA_BYTES];
State[0] = StateUpdate();
State[0].byte_pair <== [data[0], data[1]];
State[0].parsing_start <== 1;
State[0].parsing_header <== 0;
State[0].parsing_body <== 0;
State[0].read_clrf <== 0;

for(var data_idx = 1; data_idx < DATA_BYTES - 1; data_idx++) {
State[data_idx] = StateUpdate();
State[data_idx].byte_pair <== [data[data_idx], data[data_idx + 1]];
State[data_idx].parsing_start <== State[data_idx - 1].next_parsing_start;
State[data_idx].parsing_header <== State[data_idx - 1].next_parsing_header;
State[data_idx].parsing_body <== State[data_idx - 1].next_parsing_body;
State[data_idx].read_clrf <== State[data_idx - 1].next_read_clrf;

// Debugging
log("State[", data_idx, "].parsing_start ", "= ", State[data_idx].parsing_start);
log("State[", data_idx, "].parsing_header", "= ", State[data_idx].parsing_header);
log("State[", data_idx, "].parsing_body ", "= ", State[data_idx].parsing_body);
log("State[", data_idx, "].read_clrf ", "= ", State[data_idx].read_clrf);
log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
log("MethodTag: ", ParseMethod.MethodTag);

// Constrain to have valid JSON (TODO: more is needed)
// State[DATA_BYTES - 1].next_tree_depth === 0;

// // Debugging
// log("State[", DATA_BYTES, "].parsing_start ", "= ", State[DATA_BYTES-1].next_parsing_start);
// log("State[", DATA_BYTES, "].parsing_header", "= ", State[DATA_BYTES-1].next_parsing_header);
// log("State[", DATA_BYTES, "].parsing_body ", "= ", State[DATA_BYTES-1].next_parsing_body);
// log("State[", DATA_BYTES, "].read_clrf ", "= ", State[DATA_BYTES-1].next_read_clrf);
// log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

}
6 changes: 2 additions & 4 deletions examples/http/get_request.http
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
GET /objectserver/restapi/alerts/status HTTP/1.1
GET /api HTTP/1.1
Accept: application/json
Authorization: Basic dGVzdHVzZXIwMTpuZXRjb29s
Host: localhost
Connection: keep-alive
Host: localhost
15 changes: 12 additions & 3 deletions src/bin/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ struct Args {
command: Command,

/// Output directory (will be created if it doesn't exist)
#[arg(global = true, short, long, default_value = ".")]
#[arg(global = true, long, default_value = ".")]
output_dir: PathBuf,

/// Output filename (will be created if it doesn't exist)
#[arg(global = true, short, long, default_value = "output.json")]
#[arg(global = true, long, default_value = "output.json")]
output_filename: String,
}

Expand Down Expand Up @@ -64,7 +64,16 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
(data, keys_map)
}
Command::Http { input_file } => {
let data = std::fs::read(input_file)?;
let mut data = std::fs::read(input_file)?;
let mut i = 0;
while i < data.len() {
if data[i] == 10 && (i == 0 || data[i - 1] != 13) {
data.insert(i, 13);
i += 2;
} else {
i += 1;
}
}
let keys_map = serde_json::Map::new();
(data, keys_map)
}
Expand Down

0 comments on commit f01c685

Please sign in to comment.