This repository has been archived by the owner on Nov 9, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
/
meta_contract.c
169 lines (148 loc) · 5.17 KB
/
meta_contract.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
* Meta contract
* This contract is builtin in the Godwoken Rollup, and the account_id is zero.
*
* We use Meta contract to implement some special features like create a
* contract account.
*/
#include "ckb_syscalls.h"
#include "gw_eth_addr_reg.h"
#include "gw_syscalls.h"
#include "sudt_utils.h"
/* MSG_TYPE */
#define MSG_CREATE_ACCOUNT 0
#define MSG_BATCH_CREATE_ACCOUNTS 1
int handle_fee(gw_context_t *ctx, uint32_t registry_id, uint256_t amount) {
if (ctx == NULL) {
return GW_FATAL_INVALID_CONTEXT;
}
/* payer's registry address */
uint8_t payer_script_hash[32] = {0};
int ret = ctx->sys_get_script_hash_by_account_id(
ctx, ctx->transaction_context.from_id, payer_script_hash);
if (ret != 0) {
ckb_debug("failed to get script hash");
return ret;
}
gw_reg_addr_t payer_addr;
ret = ctx->sys_get_registry_address_by_script_hash(ctx, payer_script_hash,
registry_id, &payer_addr);
if (ret != 0) {
ckb_debug("failed to get payer registry address");
return ret;
}
/* pay fee */
ret = sudt_pay_fee(ctx, CKB_SUDT_ACCOUNT_ID, payer_addr, amount);
if (ret != 0) {
ckb_debug("failed to pay fee");
return ret;
}
return 0;
}
int main() {
/* initialize context */
gw_context_t ctx = {0};
int ret = gw_context_init(&ctx);
if (ret != 0) {
ckb_debug("failed to init gw context");
return ret;
};
/* return error if contract account id isn't zero */
if (ctx.transaction_context.to_id != 0) {
return GW_FATAL_INVALID_CONTEXT;
}
/* parse Meta contract args */
mol_seg_t args_seg;
args_seg.ptr = ctx.transaction_context.args;
args_seg.size = ctx.transaction_context.args_len;
if (MolReader_MetaContractArgs_verify(&args_seg, false) != MOL_OK) {
return GW_FATAL_INVALID_DATA;
}
mol_union_t msg = MolReader_MetaContractArgs_unpack(&args_seg);
/* Handle messages */
if (msg.item_id == MSG_CREATE_ACCOUNT) {
/* Charge fee */
mol_seg_t fee_seg = MolReader_CreateAccount_get_fee(&msg.seg);
mol_seg_t amount_seg = MolReader_Fee_get_amount(&fee_seg);
mol_seg_t reg_id_seg = MolReader_Fee_get_registry_id(&fee_seg);
uint256_t fee_amount = {0};
_gw_fast_memcpy((uint8_t *)(&fee_amount), (uint8_t *)amount_seg.ptr,
sizeof(uint128_t));
uint32_t reg_id = 0;
_gw_fast_memcpy((uint8_t *)(®_id), reg_id_seg.ptr, sizeof(uint32_t));
ret = handle_fee(&ctx, reg_id, fee_amount);
if (ret != 0) {
ckb_debug("failed to handle fee");
return ret;
}
/* Create account */
mol_seg_t script_seg = MolReader_CreateAccount_get_script(&msg.seg);
uint32_t account_id = 0;
ret = ctx.sys_create(&ctx, script_seg.ptr, script_seg.size, &account_id);
if (ret != 0) {
ckb_debug("failed to create account");
return ret;
}
ret = ctx.sys_set_program_return_data(&ctx, (uint8_t *)&account_id,
sizeof(uint32_t));
if (ret != 0) {
ckb_debug("failed to set return data");
return ret;
}
} else if (msg.item_id == MSG_BATCH_CREATE_ACCOUNTS) {
/* charge fee */
mol_seg_t fee_seg = MolReader_BatchCreateEthAccounts_get_fee(&msg.seg);
mol_seg_t amount_seg = MolReader_Fee_get_amount(&fee_seg);
mol_seg_t reg_id_seg = MolReader_Fee_get_registry_id(&fee_seg);
uint256_t fee_amount = {0};
_gw_fast_memcpy((uint8_t *)(&fee_amount), (uint8_t *)amount_seg.ptr,
sizeof(uint128_t));
uint32_t reg_id = 0;
_gw_fast_memcpy((uint8_t *)(®_id), (uint8_t *)reg_id_seg.ptr,
sizeof(uint32_t));
ret = handle_fee(&ctx, reg_id, fee_amount);
if (ret != 0) {
ckb_debug("failed to handle fee");
return ret;
}
/* create accounts */
mol_seg_t scripts_seg =
MolReader_BatchCreateEthAccounts_get_scripts(&msg.seg);
uint32_t scripts_size = MolReader_ScriptVec_length(&scripts_seg);
uint32_t account_id = 0;
uint8_t account_script_hash[32] = {0};
for (uint32_t i = 0; i < scripts_size; i++) {
mol_seg_res_t script_res = MolReader_ScriptVec_get(&scripts_seg, i);
if (script_res.errno != MOL_OK) {
ckb_debug("invalid account script");
return GW_FATAL_INVALID_DATA;
}
ret = ctx.sys_create(&ctx, script_res.seg.ptr, script_res.seg.size,
&account_id);
if (ret != 0) {
ckb_debug("failed to create eth account");
return ret;
}
ret = ctx.sys_get_script_hash_by_account_id(&ctx, account_id,
account_script_hash);
if (ret != 0) {
ckb_debug("failed to get created eth account script hash");
return ret;
}
ret = gw_register_eth_address(&ctx, account_script_hash);
if (ret != 0) {
ckb_debug("failed to register eth address");
return ret;
}
}
ret = ctx.sys_set_program_return_data(&ctx, (uint8_t *)&account_id,
sizeof(uint32_t));
if (ret != 0) {
ckb_debug("failed to set return data to last created eth account id");
return ret;
}
} else {
return GW_FATAL_UNKNOWN_ARGS;
}
return gw_finalize(&ctx);
}