openzoo 0.51.4 → 0.51.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/bin/openzoo.js +21 -46
- package/lib/chatgpt-install.js +106 -0
- package/lib/chatgpt.js +108 -90
- package/lib/config.js +34 -0
- package/lib/modelcommand.js +443 -0
- package/lib/pay.js +60 -4
- package/lib/proxy.js +39 -0
- package/package.json +4 -7
- package/transmute/README.md +0 -385
- package/transmute/bin/openzoo-transmute.js +0 -7
- package/transmute/docs/OPENZOO_CLI_PATCH.md +0 -139
- package/transmute/docs/VERCEL_TO_SOLANA.md +0 -381
- package/transmute/lib/build.js +0 -381
- package/transmute/lib/cli.js +0 -293
- package/transmute/lib/compile/bytecode.js +0 -591
- package/transmute/lib/compile/hostids.js +0 -11
- package/transmute/lib/compile/index.js +0 -159
- package/transmute/lib/compile/ir.js +0 -1634
- package/transmute/lib/compile/parse.js +0 -753
- package/transmute/lib/compile/rust.js +0 -781
- package/transmute/lib/deploy.js +0 -357
- package/transmute/lib/eligibility.js +0 -211
- package/transmute/lib/gateway.js +0 -710
- package/transmute/lib/hub.js +0 -290
- package/transmute/lib/solana.js +0 -387
- package/transmute/lib/vercel.js +0 -296
- package/transmute/lib/wallet.js +0 -41
- package/transmute/lib/wire.js +0 -208
- package/transmute/prebuilt/zoo_vm.v0.so +0 -0
- package/transmute/runtime/zoo-host/Cargo.lock +0 -588
- package/transmute/runtime/zoo-host/Cargo.toml +0 -30
- package/transmute/runtime/zoo-host/examples/vmrun.rs +0 -43
- package/transmute/runtime/zoo-host/src/assets.rs +0 -214
- package/transmute/runtime/zoo-host/src/ctx.rs +0 -350
- package/transmute/runtime/zoo-host/src/fmt.rs +0 -188
- package/transmute/runtime/zoo-host/src/helpers.rs +0 -245
- package/transmute/runtime/zoo-host/src/json.rs +0 -263
- package/transmute/runtime/zoo-host/src/kv.rs +0 -206
- package/transmute/runtime/zoo-host/src/lib.rs +0 -137
- package/transmute/runtime/zoo-host/src/site.rs +0 -94
- package/transmute/runtime/zoo-host/src/val.rs +0 -1050
- package/transmute/runtime/zoo-host/src/vm.rs +0 -902
- package/transmute/runtime/zoo-host/src/wire.rs +0 -239
- package/transmute/runtime/zoo-vm/Cargo.lock +0 -596
- package/transmute/runtime/zoo-vm/Cargo.toml +0 -23
- package/transmute/runtime/zoo-vm/src/lib.rs +0 -92
- package/transmute/site/.env.production +0 -2
- package/transmute/site/api/echo.js +0 -10
- package/transmute/site/api/hello.js +0 -12
- package/transmute/site/api/hits.js +0 -13
- package/transmute/site/api/time.js +0 -5
- package/transmute/site/package.json +0 -6
- package/transmute/site/public/app.js +0 -16
- package/transmute/site/public/index.html +0 -96
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
//! `@vercel/kv` on PDAs.
|
|
2
|
-
//!
|
|
3
|
-
//! Key → PDA `["kv", sha256(key)]`. Account data:
|
|
4
|
-
//! `[u8 ver=1][u8 bump][u32 len][len bytes of JSON]`.
|
|
5
|
-
//!
|
|
6
|
-
//! The gateway does not know which keys a handler will touch, so the program
|
|
7
|
-
//! discovers them: a key whose PDA is not in the account list is recorded in
|
|
8
|
-
//! [`KvState::missing`] and the invoke fails with `ERR_KV_MISSING`; the
|
|
9
|
-
//! gateway reads the `ZOOK` logs, appends those PDAs and retries. Accounts
|
|
10
|
-
//! `0` and `1` are the payer and the system program.
|
|
11
|
-
use crate::json;
|
|
12
|
-
use crate::val::Val;
|
|
13
|
-
use crate::Ctx;
|
|
14
|
-
use alloc::{string::String, vec::Vec};
|
|
15
|
-
use pinocchio::{
|
|
16
|
-
cpi::{Seed, Signer},
|
|
17
|
-
sysvars::{rent::Rent, Sysvar},
|
|
18
|
-
Address, Resize,
|
|
19
|
-
};
|
|
20
|
-
use pinocchio_system::instructions::{CreateAccount, Transfer};
|
|
21
|
-
|
|
22
|
-
pub const HEADER: usize = 6;
|
|
23
|
-
pub const MAX_VALUE: usize = 10_000;
|
|
24
|
-
|
|
25
|
-
#[derive(Default)]
|
|
26
|
-
pub struct KvState {
|
|
27
|
-
pub missing: Vec<Address>,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/// The sentinel thrown into the handler when a KV account is missing; the
|
|
31
|
-
/// dispatcher looks at `missing` first, so this never reaches the response.
|
|
32
|
-
pub const MISSING_SENTINEL: &str = "__ZOO_KV_MISSING__";
|
|
33
|
-
|
|
34
|
-
pub fn key_hash(key: &str) -> [u8; 32] {
|
|
35
|
-
solana_sha256_hasher::hashv(&[b"zoo-kv", key.as_bytes()]).to_bytes()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
pub fn pda_for(program_id: &Address, key: &str) -> (Address, u8, [u8; 32]) {
|
|
39
|
-
pda_for_ns(program_id, None, key)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/// KV PDA: `["kv", hash]` for a compiled site, `["kv", site_id, hash]` under
|
|
43
|
-
/// the shared runtime.
|
|
44
|
-
pub fn pda_for_ns(program_id: &Address, ns: Option<&[u8; 32]>, key: &str) -> (Address, u8, [u8; 32]) {
|
|
45
|
-
let h = key_hash(key);
|
|
46
|
-
let (a, bump) = match ns {
|
|
47
|
-
Some(n) => Address::find_program_address(&[b"kv", n, &h], program_id),
|
|
48
|
-
None => Address::find_program_address(&[b"kv", &h], program_id),
|
|
49
|
-
};
|
|
50
|
-
(a, bump, h)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
struct Slot {
|
|
54
|
-
idx: usize,
|
|
55
|
-
bump: u8,
|
|
56
|
-
hash: [u8; 32],
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
impl<'a> Ctx<'a> {
|
|
60
|
-
fn kv_slot(&mut self, key: &Val) -> Result<Slot, Val> {
|
|
61
|
-
let k = key.to_js_string();
|
|
62
|
-
let (addr, bump, hash) = pda_for_ns(self.program_id, self.ns.as_ref(), &k);
|
|
63
|
-
match self.accounts.iter().position(|a| *a.address() == addr) {
|
|
64
|
-
Some(idx) => Ok(Slot { idx, bump, hash }),
|
|
65
|
-
None => {
|
|
66
|
-
if !self.kv.missing.iter().any(|m| *m == addr) {
|
|
67
|
-
self.kv.missing.push(addr);
|
|
68
|
-
}
|
|
69
|
-
Err(Val::str(MISSING_SENTINEL))
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
fn kv_read(&self, idx: usize) -> Val {
|
|
75
|
-
let acc = &self.accounts[idx];
|
|
76
|
-
if !acc.owned_by(self.program_id) || acc.data_len() < HEADER {
|
|
77
|
-
return Val::Null;
|
|
78
|
-
}
|
|
79
|
-
let d = match acc.try_borrow() {
|
|
80
|
-
Ok(d) => d,
|
|
81
|
-
Err(_) => return Val::Null,
|
|
82
|
-
};
|
|
83
|
-
let len = u32::from_le_bytes([d[2], d[3], d[4], d[5]]) as usize;
|
|
84
|
-
if len == 0 || HEADER + len > d.len() {
|
|
85
|
-
return Val::Null;
|
|
86
|
-
}
|
|
87
|
-
let text = match core::str::from_utf8(&d[HEADER..HEADER + len]) {
|
|
88
|
-
Ok(t) => t,
|
|
89
|
-
Err(_) => return Val::Null,
|
|
90
|
-
};
|
|
91
|
-
json::parse(text).unwrap_or(Val::Null)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/// `kv.get(key)` → value or null.
|
|
95
|
-
pub fn kv_get(&mut self, key: &Val) -> Result<Val, Val> {
|
|
96
|
-
let s = self.kv_slot(key)?;
|
|
97
|
-
Ok(self.kv_read(s.idx))
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/// `kv.exists(key)` → 0 | 1
|
|
101
|
-
pub fn kv_exists(&mut self, key: &Val) -> Result<Val, Val> {
|
|
102
|
-
let s = self.kv_slot(key)?;
|
|
103
|
-
Ok(Val::Num(if self.kv_read(s.idx).is_nullish() { 0.0 } else { 1.0 }))
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/// `kv.set(key, value)` → "OK"
|
|
107
|
-
pub fn kv_set(&mut self, key: &Val, value: &Val) -> Result<Val, Val> {
|
|
108
|
-
let s = self.kv_slot(key)?;
|
|
109
|
-
let payload = json::stringify(value).into_bytes();
|
|
110
|
-
if payload.len() > MAX_VALUE {
|
|
111
|
-
return Err(Val::str("kv.set: value exceeds 10000 bytes"));
|
|
112
|
-
}
|
|
113
|
-
self.kv_write(&s, &payload)?;
|
|
114
|
-
Ok(Val::str("OK"))
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/// `kv.incr(key)` / `kv.incrby(key, n)` / `kv.decr`
|
|
118
|
-
pub fn kv_incrby(&mut self, key: &Val, by: &Val) -> Result<Val, Val> {
|
|
119
|
-
let s = self.kv_slot(key)?;
|
|
120
|
-
let cur = self.kv_read(s.idx);
|
|
121
|
-
let n = if cur.is_nullish() { 0.0 } else { cur.to_num() };
|
|
122
|
-
if n.is_nan() {
|
|
123
|
-
return Err(Val::str("ERR value is not an integer or out of range"));
|
|
124
|
-
}
|
|
125
|
-
let next = Val::Num(n + by.to_num());
|
|
126
|
-
let payload = json::stringify(&next).into_bytes();
|
|
127
|
-
self.kv_write(&s, &payload)?;
|
|
128
|
-
Ok(next)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/// `kv.del(key)` → number of keys removed (0 | 1)
|
|
132
|
-
pub fn kv_del(&mut self, key: &Val) -> Result<Val, Val> {
|
|
133
|
-
let s = self.kv_slot(key)?;
|
|
134
|
-
if self.kv_read(s.idx).is_nullish() {
|
|
135
|
-
return Ok(Val::Num(0.0));
|
|
136
|
-
}
|
|
137
|
-
self.kv_write(&s, &[])?;
|
|
138
|
-
Ok(Val::Num(1.0))
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
fn kv_write(&mut self, s: &Slot, payload: &[u8]) -> Result<(), Val> {
|
|
142
|
-
let need = HEADER + payload.len();
|
|
143
|
-
let program_id = self.program_id;
|
|
144
|
-
let bump = [s.bump];
|
|
145
|
-
if self.accounts.len() < 2 {
|
|
146
|
-
return Err(Val::str("kv.set: payer and system program accounts required"));
|
|
147
|
-
}
|
|
148
|
-
if s.idx < 2 {
|
|
149
|
-
return Err(Val::str("kv: account layout violated"));
|
|
150
|
-
}
|
|
151
|
-
let (head, tail) = self.accounts.split_at_mut(s.idx);
|
|
152
|
-
let payer = &head[0];
|
|
153
|
-
let acc = &mut tail[0];
|
|
154
|
-
if !acc.owned_by(program_id) {
|
|
155
|
-
// First write: create the PDA.
|
|
156
|
-
if !payer.is_signer() {
|
|
157
|
-
return Err(Val::str("kv.set: payer must sign"));
|
|
158
|
-
}
|
|
159
|
-
let ns = self.ns;
|
|
160
|
-
let r = match ns.as_ref() {
|
|
161
|
-
Some(n) => {
|
|
162
|
-
let seeds = [Seed::from(b"kv".as_ref()), Seed::from(n.as_ref()), Seed::from(s.hash.as_ref()), Seed::from(bump.as_ref())];
|
|
163
|
-
let signer = Signer::from(&seeds);
|
|
164
|
-
CreateAccount::with_minimum_balance(payer, acc, need as u64, program_id, None)
|
|
165
|
-
.map_err(|e| Val::Str(err_str("kv create", e)))?
|
|
166
|
-
.invoke_signed(&[signer])
|
|
167
|
-
}
|
|
168
|
-
None => {
|
|
169
|
-
let seeds = [Seed::from(b"kv".as_ref()), Seed::from(s.hash.as_ref()), Seed::from(bump.as_ref())];
|
|
170
|
-
let signer = Signer::from(&seeds);
|
|
171
|
-
CreateAccount::with_minimum_balance(payer, acc, need as u64, program_id, None)
|
|
172
|
-
.map_err(|e| Val::Str(err_str("kv create", e)))?
|
|
173
|
-
.invoke_signed(&[signer])
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
r.map_err(|e| Val::Str(err_str("kv create", e)))?;
|
|
177
|
-
} else if need > acc.data_len() {
|
|
178
|
-
let rent = Rent::get().map_err(|e| Val::Str(err_str("rent", e)))?;
|
|
179
|
-
let min = rent.try_minimum_balance(need).map_err(|e| Val::Str(err_str("rent", e)))?;
|
|
180
|
-
if min > acc.lamports() {
|
|
181
|
-
Transfer { from: payer, to: acc, lamports: min - acc.lamports() }
|
|
182
|
-
.invoke()
|
|
183
|
-
.map_err(|e| Val::Str(err_str("kv rent", e)))?;
|
|
184
|
-
}
|
|
185
|
-
acc.resize(need).map_err(|e| Val::Str(err_str("kv resize", e)))?;
|
|
186
|
-
} else if need < acc.data_len() && payload.is_empty() {
|
|
187
|
-
acc.resize(HEADER).map_err(|e| Val::Str(err_str("kv resize", e)))?;
|
|
188
|
-
}
|
|
189
|
-
let mut d = acc.try_borrow_mut().map_err(|e| Val::Str(err_str("kv borrow", e)))?;
|
|
190
|
-
d[0] = 1;
|
|
191
|
-
d[1] = s.bump;
|
|
192
|
-
d[2..6].copy_from_slice(&(payload.len() as u32).to_le_bytes());
|
|
193
|
-
if !payload.is_empty() {
|
|
194
|
-
d[HEADER..HEADER + payload.len()].copy_from_slice(payload);
|
|
195
|
-
}
|
|
196
|
-
Ok(())
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
pub fn err_str(what: &str, e: pinocchio::error::ProgramError) -> String {
|
|
201
|
-
let code: u64 = e.into();
|
|
202
|
-
let mut m = String::from(what);
|
|
203
|
-
m.push_str(" failed: program error ");
|
|
204
|
-
crate::fmt::push_u64(&mut m, code);
|
|
205
|
-
m
|
|
206
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
//! zoo-host: the on-chain half of openzoo-transmute.
|
|
2
|
-
//!
|
|
3
|
-
//! A Vercel deployment is, in Vercel's own terms, a set of Lambdas (`*.func`)
|
|
4
|
-
//! plus a `static/` tree plus a `config.json` route table. Vercel's node
|
|
5
|
-
//! bridge feeds each Lambda an `Invoke` event `{method, path, headers, body}`
|
|
6
|
-
//! and expects `{statusCode, headers, body}` back. This crate is that bridge,
|
|
7
|
-
//! re-hosted on Solana:
|
|
8
|
-
//!
|
|
9
|
-
//! * the Invoke event is the instruction data ([`wire::Req`])
|
|
10
|
-
//! * the bridge response is the return data + `sol_log_data` chunks
|
|
11
|
-
//! ([`wire::Resp`])
|
|
12
|
-
//! * `static/` lives in asset PDAs ([`assets`])
|
|
13
|
-
//! * `@vercel/kv` lives in KV PDAs ([`kv`])
|
|
14
|
-
//! * the handler body is Rust generated by the transmuter, operating on a
|
|
15
|
-
//! JS-semantics dynamic value ([`val::Val`])
|
|
16
|
-
//!
|
|
17
|
-
//! Generated programs call [`dispatch`] from their entrypoint.
|
|
18
|
-
#![no_std]
|
|
19
|
-
#![allow(clippy::needless_range_loop)]
|
|
20
|
-
|
|
21
|
-
extern crate alloc;
|
|
22
|
-
|
|
23
|
-
pub mod assets;
|
|
24
|
-
pub mod ctx;
|
|
25
|
-
pub mod fmt;
|
|
26
|
-
pub mod helpers;
|
|
27
|
-
pub mod site;
|
|
28
|
-
pub mod vm;
|
|
29
|
-
pub mod json;
|
|
30
|
-
pub mod kv;
|
|
31
|
-
pub mod val;
|
|
32
|
-
pub mod wire;
|
|
33
|
-
|
|
34
|
-
use alloc::{string::String, vec::Vec};
|
|
35
|
-
use pinocchio::{error::ProgramError, AccountView, Address, ProgramResult};
|
|
36
|
-
|
|
37
|
-
pub use ctx::Ctx;
|
|
38
|
-
pub use val::Val;
|
|
39
|
-
pub use wire::{Req, Resp};
|
|
40
|
-
|
|
41
|
-
/// A transmuted route handler. `Err(v)` is a thrown JS value.
|
|
42
|
-
pub type Route = fn(&mut Ctx) -> Result<(), Val>;
|
|
43
|
-
|
|
44
|
-
/// Instruction tags (first byte of instruction data).
|
|
45
|
-
pub const TAG_INVOKE: u8 = 0;
|
|
46
|
-
pub const TAG_ASSET_INIT: u8 = 1;
|
|
47
|
-
pub const TAG_ASSET_WRITE: u8 = 2;
|
|
48
|
-
pub const TAG_ASSET_CLOSE: u8 = 3;
|
|
49
|
-
// Shared runtime (zoo-vm) tags. Instruction data after the tag starts with
|
|
50
|
-
// the 32-byte site id, then the same payloads as the compiled-site tags.
|
|
51
|
-
pub const TAG_SITE_INIT: u8 = 16;
|
|
52
|
-
pub const TAG_SITE_SET_AUTHORITY: u8 = 17;
|
|
53
|
-
pub const TAG_VM_INVOKE: u8 = 18;
|
|
54
|
-
pub const TAG_VM_ASSET_INIT: u8 = 19;
|
|
55
|
-
pub const TAG_VM_ASSET_WRITE: u8 = 20;
|
|
56
|
-
pub const TAG_VM_ASSET_CLOSE: u8 = 21;
|
|
57
|
-
/// Path of a site's bytecode asset under the shared runtime.
|
|
58
|
-
pub const CODE_PATH: &str = "/.zoo/code.bin";
|
|
59
|
-
|
|
60
|
-
/// Custom program error: a KV account the handler needed was not passed.
|
|
61
|
-
/// The gateway reads the `ZOOK` log lines, adds the accounts and retries.
|
|
62
|
-
pub const ERR_KV_MISSING: u32 = 0x4b56_0001;
|
|
63
|
-
/// Custom program error: the caller is not the program's upgrade authority.
|
|
64
|
-
pub const ERR_NOT_AUTHORITY: u32 = 0x4b56_0002;
|
|
65
|
-
/// Custom program error: malformed instruction data.
|
|
66
|
-
pub const ERR_BAD_WIRE: u32 = 0x4b56_0003;
|
|
67
|
-
|
|
68
|
-
/// Entry used by generated programs.
|
|
69
|
-
///
|
|
70
|
-
/// `env` is the Lambda's `.vc-config.json` `environment` table, baked in at
|
|
71
|
-
/// build time.
|
|
72
|
-
pub fn dispatch(
|
|
73
|
-
program_id: &Address,
|
|
74
|
-
accounts: &mut [AccountView],
|
|
75
|
-
data: &[u8],
|
|
76
|
-
routes: &[Route],
|
|
77
|
-
env: &[(&'static str, &'static str)],
|
|
78
|
-
) -> ProgramResult {
|
|
79
|
-
let (tag, rest) = match data.split_first() {
|
|
80
|
-
Some(x) => x,
|
|
81
|
-
None => return Err(ProgramError::Custom(ERR_BAD_WIRE)),
|
|
82
|
-
};
|
|
83
|
-
match *tag {
|
|
84
|
-
TAG_INVOKE => invoke(program_id, accounts, rest, routes, env),
|
|
85
|
-
TAG_ASSET_INIT => assets::init(program_id, accounts, rest),
|
|
86
|
-
TAG_ASSET_WRITE => assets::write(program_id, accounts, rest),
|
|
87
|
-
TAG_ASSET_CLOSE => assets::close(program_id, accounts, rest),
|
|
88
|
-
_ => Err(ProgramError::InvalidInstructionData),
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
fn invoke(
|
|
93
|
-
program_id: &Address,
|
|
94
|
-
accounts: &mut [AccountView],
|
|
95
|
-
data: &[u8],
|
|
96
|
-
routes: &[Route],
|
|
97
|
-
env: &[(&'static str, &'static str)],
|
|
98
|
-
) -> ProgramResult {
|
|
99
|
-
let req = wire::parse_req(data).map_err(|_| ProgramError::Custom(ERR_BAD_WIRE))?;
|
|
100
|
-
let route = routes
|
|
101
|
-
.get(req.route as usize)
|
|
102
|
-
.copied()
|
|
103
|
-
.ok_or(ProgramError::InvalidInstructionData)?;
|
|
104
|
-
let mut cx = Ctx::new(program_id, accounts, req, env);
|
|
105
|
-
let outcome = route(&mut cx);
|
|
106
|
-
if !cx.kv.missing.is_empty() {
|
|
107
|
-
// Account discovery: tell the gateway which PDAs to add, then fail so
|
|
108
|
-
// nothing half-applied is committed.
|
|
109
|
-
for pda in cx.kv.missing.iter() {
|
|
110
|
-
wire::log_kv_missing(pda);
|
|
111
|
-
}
|
|
112
|
-
return Err(ProgramError::Custom(ERR_KV_MISSING));
|
|
113
|
-
}
|
|
114
|
-
match outcome {
|
|
115
|
-
Ok(()) => {}
|
|
116
|
-
Err(thrown) => {
|
|
117
|
-
// Uncaught throw == a Lambda crash == 500, same as Vercel.
|
|
118
|
-
let msg: String = match &thrown {
|
|
119
|
-
Val::Obj(_) => match thrown.get_str("message") {
|
|
120
|
-
Val::Undef => thrown.to_js_string(),
|
|
121
|
-
m => m.to_js_string(),
|
|
122
|
-
},
|
|
123
|
-
_ => thrown.to_js_string(),
|
|
124
|
-
};
|
|
125
|
-
cx.res = Resp::json_error(500, &msg);
|
|
126
|
-
cx.sent = true;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if !cx.sent {
|
|
130
|
-
// Handler returned without responding: Vercel answers 504 for a
|
|
131
|
-
// Lambda that never called res.end(); we mirror that.
|
|
132
|
-
cx.res = Resp::json_error(504, "handler returned without a response");
|
|
133
|
-
}
|
|
134
|
-
let bytes: Vec<u8> = wire::encode_resp(&cx.res);
|
|
135
|
-
wire::emit(&bytes);
|
|
136
|
-
Ok(())
|
|
137
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
//! The shared runtime's site registry.
|
|
2
|
-
//!
|
|
3
|
-
//! Under the shared runtime program a site is not a program: it is a site
|
|
4
|
-
//! account `["site", site_id]` naming its authority, a code asset
|
|
5
|
-
//! (`/.zoo/code.bin`, the bytecode module), its static assets and its
|
|
6
|
-
//! manifest, all PDAs of the runtime program namespaced by `site_id`, plus KV
|
|
7
|
-
//! accounts `["kv", site_id, hash]`. `site_id` is any 32-byte id the deployer
|
|
8
|
-
//! picks (the CLI uses a fresh keypair's public key so ids look like programs).
|
|
9
|
-
use crate::ERR_NOT_AUTHORITY;
|
|
10
|
-
use pinocchio::{
|
|
11
|
-
cpi::{Seed, Signer},
|
|
12
|
-
error::ProgramError,
|
|
13
|
-
AccountView, Address, ProgramResult,
|
|
14
|
-
};
|
|
15
|
-
use pinocchio_system::instructions::CreateAccount;
|
|
16
|
-
|
|
17
|
-
pub const SITE_LEN: usize = 2 + 32 + 32; // ver, bump, authority, reserved
|
|
18
|
-
|
|
19
|
-
pub fn site_pda(program_id: &Address, site_id: &[u8; 32]) -> (Address, u8) {
|
|
20
|
-
Address::find_program_address(&[b"site", site_id], program_id)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/// `accounts[0]` must sign as the authority recorded in the site account at `accounts[1]`.
|
|
24
|
-
pub fn check_site_authority(program_id: &Address, accounts: &[AccountView], site_id: &[u8; 32]) -> ProgramResult {
|
|
25
|
-
let authority = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
|
|
26
|
-
let site = accounts.get(1).ok_or(ProgramError::NotEnoughAccountKeys)?;
|
|
27
|
-
if !authority.is_signer() {
|
|
28
|
-
return Err(ProgramError::MissingRequiredSignature);
|
|
29
|
-
}
|
|
30
|
-
let (expected, _) = site_pda(program_id, site_id);
|
|
31
|
-
if *site.address() != expected || !site.owned_by(program_id) {
|
|
32
|
-
return Err(ProgramError::Custom(ERR_NOT_AUTHORITY));
|
|
33
|
-
}
|
|
34
|
-
let d = site.try_borrow()?;
|
|
35
|
-
if d.len() < SITE_LEN || d[0] != 1 || d[2..34] != authority.address().as_ref()[..] {
|
|
36
|
-
return Err(ProgramError::Custom(ERR_NOT_AUTHORITY));
|
|
37
|
-
}
|
|
38
|
-
Ok(())
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/// `[32 site_id]`; accounts: authority (signer, pays), site PDA (writable), system program.
|
|
42
|
-
/// Creates the site account owned by the runtime with `authority` in charge.
|
|
43
|
-
pub fn init(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult {
|
|
44
|
-
if data.len() < 32 {
|
|
45
|
-
return Err(ProgramError::InvalidInstructionData);
|
|
46
|
-
}
|
|
47
|
-
let mut site_id = [0u8; 32];
|
|
48
|
-
site_id.copy_from_slice(&data[..32]);
|
|
49
|
-
let (head, tail) = accounts.split_at_mut(1);
|
|
50
|
-
let authority = &head[0];
|
|
51
|
-
let site = tail.first_mut().ok_or(ProgramError::NotEnoughAccountKeys)?;
|
|
52
|
-
if !authority.is_signer() {
|
|
53
|
-
return Err(ProgramError::MissingRequiredSignature);
|
|
54
|
-
}
|
|
55
|
-
let (addr, bump) = site_pda(program_id, &site_id);
|
|
56
|
-
if *site.address() != addr {
|
|
57
|
-
return Err(ProgramError::InvalidSeeds);
|
|
58
|
-
}
|
|
59
|
-
if site.owned_by(program_id) {
|
|
60
|
-
return Err(ProgramError::AccountAlreadyInitialized);
|
|
61
|
-
}
|
|
62
|
-
let bump_bytes = [bump];
|
|
63
|
-
let seeds = [Seed::from(b"site".as_ref()), Seed::from(site_id.as_ref()), Seed::from(bump_bytes.as_ref())];
|
|
64
|
-
let signer = Signer::from(&seeds);
|
|
65
|
-
CreateAccount::with_minimum_balance(authority, site, SITE_LEN as u64, program_id, None)?.invoke_signed(&[signer])?;
|
|
66
|
-
let mut d = site.try_borrow_mut()?;
|
|
67
|
-
d[0] = 1;
|
|
68
|
-
d[1] = bump;
|
|
69
|
-
d[2..34].copy_from_slice(authority.address().as_ref());
|
|
70
|
-
Ok(())
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/// `[32 site_id][32 new_authority]`; accounts: authority (signer), site PDA (writable).
|
|
74
|
-
pub fn set_authority(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult {
|
|
75
|
-
if data.len() < 64 {
|
|
76
|
-
return Err(ProgramError::InvalidInstructionData);
|
|
77
|
-
}
|
|
78
|
-
let mut site_id = [0u8; 32];
|
|
79
|
-
site_id.copy_from_slice(&data[..32]);
|
|
80
|
-
check_site_authority(program_id, accounts, &site_id)?;
|
|
81
|
-
let site = accounts.get_mut(1).ok_or(ProgramError::NotEnoughAccountKeys)?;
|
|
82
|
-
let mut d = site.try_borrow_mut()?;
|
|
83
|
-
d[2..34].copy_from_slice(&data[32..64]);
|
|
84
|
-
Ok(())
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
pub fn site_id_from(data: &[u8]) -> Result<([u8; 32], &[u8]), ProgramError> {
|
|
88
|
-
if data.len() < 32 {
|
|
89
|
-
return Err(ProgramError::InvalidInstructionData);
|
|
90
|
-
}
|
|
91
|
-
let mut id = [0u8; 32];
|
|
92
|
-
id.copy_from_slice(&data[..32]);
|
|
93
|
-
Ok((id, &data[32..]))
|
|
94
|
-
}
|