Skip to content

Commit 2bd8646

Browse files
Merge pull request #1021 from LedgerHQ/ref/712_iterative_type_hash
Move away from recursive calls when computing the EIP-712 type_hash
2 parents 31e3b60 + 0f28fdd commit 2bd8646

1 file changed

Lines changed: 62 additions & 38 deletions

File tree

src/features/sign_message_eip712/type_hash.c

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,60 +69,84 @@ typedef struct struct_dep {
6969
} s_struct_dep;
7070

7171
/**
72-
* Find all the dependencies from a given structure
72+
* Add a struct to the dependency list if not already present
7373
*
74-
* @param[out] deps_count count of how many struct dependency pointers
75-
* @param[in] first_dep pointer to the first dependency pointer
76-
* @param[in] struct_ptr pointer to the struct we are getting the dependencies of
77-
* @return pointer to the first found dependency, \ref NULL otherwise
74+
* @param[in,out] first_dep pointer to the head of the dependency list
75+
* @param[in] struct_ptr pointer to the struct to add
76+
* @return \ref true on success, \ref false on memory allocation failure
7877
*/
79-
static bool get_struct_dependencies(s_struct_dep **first_dep, const s_struct_712 *struct_ptr) {
80-
const s_struct_712_field *field_ptr;
81-
const char *arg_structname;
82-
const s_struct_712 *arg_struct_ptr;
78+
static bool add_dep_if_new(s_struct_dep **first_dep, const s_struct_712 *struct_ptr) {
8379
s_struct_dep *tmp;
8480
s_struct_dep *new_dep;
8581

82+
for (tmp = *first_dep; tmp != NULL; tmp = (s_struct_dep *) ((flist_node_t *) tmp)->next) {
83+
if (tmp->s == struct_ptr) return true;
84+
}
85+
if ((new_dep = APP_MEM_ALLOC(sizeof(*new_dep))) == NULL) {
86+
apdu_response_code = SWO_INSUFFICIENT_MEMORY;
87+
return false;
88+
}
89+
new_dep->s = struct_ptr;
90+
flist_push_back((flist_node_t **) first_dep, (flist_node_t *) new_dep);
91+
return true;
92+
}
93+
94+
/**
95+
* Scan all fields of a struct and add any unknown TYPE_CUSTOM dependencies
96+
*
97+
* @param[in,out] first_dep pointer to the head of the dependency list
98+
* @param[in] struct_ptr pointer to the struct whose fields are scanned
99+
* @return \ref true on success, \ref false on error
100+
*/
101+
static bool collect_direct_deps(s_struct_dep **first_dep, const s_struct_712 *struct_ptr) {
102+
const s_struct_712_field *field_ptr;
103+
const s_struct_712 *dep;
104+
const char *dep_name;
105+
86106
for (field_ptr = struct_ptr->fields; field_ptr != NULL;
87107
field_ptr = (s_struct_712_field *) ((flist_node_t *) field_ptr)->next) {
88108
if (field_ptr->type == TYPE_CUSTOM) {
89-
// get struct name
90-
arg_structname = get_struct_field_typename(field_ptr);
91-
// from its name, get the pointer to its definition
92-
if ((arg_struct_ptr = get_structn(arg_structname, strlen(arg_structname))) == NULL) {
93-
PRINTF("Error: could not find EIP-712 dependency struct \"");
94-
for (int i = 0; i < (int) strlen(arg_structname); ++i)
95-
PRINTF("%c", arg_structname[i]);
96-
PRINTF("\" during type_hash\n");
109+
dep_name = get_struct_field_typename(field_ptr);
110+
if ((dep = get_structn(dep_name, strlen(dep_name))) == NULL) {
111+
PRINTF("Error: could not find EIP-712 dependency struct \"%s\" during type_hash\n",
112+
dep_name);
97113
return false;
98114
}
99-
100-
// check if it is not already present in the dependencies array
101-
for (tmp = *first_dep; tmp != NULL;
102-
tmp = (s_struct_dep *) ((flist_node_t *) tmp)->next) {
103-
// it's a match!
104-
if (tmp->s == arg_struct_ptr) {
105-
break;
106-
}
107-
}
108-
// if it's not present in the array, add it and recurse into it
109-
if (tmp == NULL) {
110-
if (APP_MEM_CALLOC((void **) &new_dep, sizeof(s_struct_dep)) == false) {
111-
apdu_response_code = SWO_INSUFFICIENT_MEMORY;
112-
return false;
113-
}
114-
new_dep->s = arg_struct_ptr;
115-
flist_push_back((flist_node_t **) first_dep, (flist_node_t *) new_dep);
116-
// TODO: Move away from recursive calls
117-
if (!get_struct_dependencies(first_dep, arg_struct_ptr)) {
118-
return false;
119-
}
115+
if (!add_dep_if_new(first_dep, dep)) {
116+
return false;
120117
}
121118
}
122119
}
123120
return true;
124121
}
125122

123+
/**
124+
* Find all the transitive dependencies of a given structure
125+
*
126+
* Uses the dependency list itself as a worklist: new entries are appended at
127+
* the back while the cursor advances forward, so newly discovered dependencies
128+
* are processed without recursion and without extra allocations.
129+
*
130+
* @param[out] first_dep pointer to the head of the dependency list
131+
* @param[in] struct_ptr pointer to the struct we are getting the dependencies of
132+
* @return \ref true on success, \ref false on error
133+
*/
134+
static bool get_struct_dependencies(s_struct_dep **first_dep, const s_struct_712 *struct_ptr) {
135+
s_struct_dep *cursor;
136+
137+
if (!collect_direct_deps(first_dep, struct_ptr)) {
138+
return false;
139+
}
140+
141+
for (cursor = *first_dep; cursor != NULL;
142+
cursor = (s_struct_dep *) ((flist_node_t *) cursor)->next) {
143+
if (!collect_direct_deps(first_dep, cursor->s)) {
144+
return false;
145+
}
146+
}
147+
return true;
148+
}
149+
126150
static bool compare_struct_deps(const s_struct_dep *a, const s_struct_dep *b) {
127151
const char *name1, *name2;
128152
size_t namelen1, namelen2;

0 commit comments

Comments
 (0)