Skip to content

Commit d2de0ab

Browse files
authored
Upd price in rust (#248)
* Works * Comments * Restore upd_aggregate * Add all tests * Good * Export upd_aggregate * Comment * Cleanup * Better comment * Oops * Update error * Fix compilation * Rename libs * Comment * Latest publisher price * Reorder * Format * Format Co-authored-by: Guillermo Bescos <guibescos>
1 parent 76e982d commit d2de0ab

File tree

13 files changed

+956
-431
lines changed

13 files changed

+956
-431
lines changed

program/c/makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ OUT_DIR := ./target
22
SOLANA := ../../../solana
33
include $(SOLANA)/sdk/bpf/c/bpf.mk
44

5-
cpyth:
5+
cpyth-bpf:
66
# Bundle C code compiled to bpf for use by rust
7-
bash -c "ar rcs target/libcpyth.a target/**/*.o"
8-
cpythtest:
7+
bash -c "ar rcs target/libcpyth-bpf.a target/**/*.o"
8+
cpyth-native:
99
# Compile C code to system architecture for use by rust's cargo test
10-
$(CC) -c ./src/oracle/for_cargo_test/cpyth_test.c -o ./target/cpyth_test.o
10+
gcc -c ./src/oracle/for_cargo_test/cpyth_test.c -o ./target/cpyth_test.o
1111
# Bundle C code compiled to system architecture for use by rust's cargo test
12-
ar rcs target/libcpythtest.a ./target/cpyth_test.o
12+
ar rcs target/libcpyth-native.a ./target/cpyth_test.o

program/c/src/oracle/test_oracle.c

Lines changed: 0 additions & 352 deletions
Original file line numberDiff line numberDiff line change
@@ -19,355 +19,3 @@ Test(oracle, pc_size ) {
1919
3*sizeof( pc_pub_key_t ) + sizeof( pc_price_info_t ) +
2020
PC_COMP_SIZE * sizeof( pc_price_comp_t ) );
2121
}
22-
23-
Test( oracle, upd_price ) {
24-
cmd_upd_price_t idata = {
25-
.ver_ = PC_VERSION,
26-
.cmd_ = e_cmd_upd_price,
27-
.status_ = PC_STATUS_TRADING,
28-
.price_ = 42L,
29-
.conf_ = 2L,
30-
.pub_slot_ = 1
31-
};
32-
SolPubkey p_id = {.x = { 0xff, }};
33-
SolPubkey pkey = {.x = { 1, }};
34-
SolPubkey skey = {.x = { 3, }};
35-
sysvar_clock_t cvar = {
36-
.slot_ = 1
37-
};
38-
uint64_t pqty = 100, sqty = 200;
39-
pc_price_t sptr[1];
40-
sol_memset( sptr, 0, sizeof( pc_price_t ) );
41-
sptr->magic_ = PC_MAGIC;
42-
sptr->ver_ = PC_VERSION;
43-
sptr->ptype_ = PC_PTYPE_PRICE;
44-
sptr->type_ = PC_ACCTYPE_PRICE;
45-
sptr->num_ = 1;
46-
pc_pub_key_assign( &sptr->comp_[0].pub_, (pc_pub_key_t*)&pkey );
47-
SolAccountInfo acc[] = {{
48-
.key = &pkey,
49-
.lamports = &pqty,
50-
.data_len = 0,
51-
.data = NULL,
52-
.owner = NULL,
53-
.rent_epoch = 0,
54-
.is_signer = true,
55-
.is_writable = true,
56-
.executable = false
57-
},{
58-
.key = &skey,
59-
.lamports = &PRICE_ACCOUNT_LAMPORTS,
60-
.data_len = sizeof( pc_price_t ),
61-
.data = (uint8_t*)sptr,
62-
.owner = &p_id,
63-
.rent_epoch = 0,
64-
.is_signer = false,
65-
.is_writable = true,
66-
.executable = false
67-
},{
68-
.key = (SolPubkey*)sysvar_clock,
69-
.lamports = &sqty,
70-
.data_len = sizeof( sysvar_clock_t ),
71-
.data = (uint8_t*)&cvar,
72-
.owner = &p_id,
73-
.rent_epoch = 0,
74-
.is_signer = false,
75-
.is_writable = false,
76-
.executable = false
77-
}};
78-
SolParameters prm = {
79-
.ka = acc,
80-
.ka_num = 3,
81-
.data = (const uint8_t*)&idata,
82-
.data_len = sizeof( idata ),
83-
.program_id = &p_id
84-
};
85-
cr_assert( SUCCESS == dispatch( &prm, acc ) );
86-
cr_assert( sptr->comp_[0].latest_.price_ == 42L );
87-
cr_assert( sptr->comp_[0].latest_.conf_ == 2L );
88-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 1 );
89-
cr_assert( sptr->agg_.pub_slot_ == 1 );
90-
cr_assert( sptr->valid_slot_ == 0 );
91-
92-
// add some prices for current slot - get rejected
93-
idata.price_ = 43;
94-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
95-
cr_assert( sptr->comp_[0].latest_.price_ == 42L );
96-
cr_assert( sptr->comp_[0].agg_.price_ == 0 );
97-
98-
// add next price in new slot triggering snapshot and aggregate calc
99-
idata.price_ = 81;
100-
idata.pub_slot_ = 2;
101-
cvar.slot_ = 3;
102-
cr_assert( SUCCESSFULLY_UPDATED_AGGREGATE == dispatch( &prm, acc ) );
103-
cr_assert( sptr->comp_[0].latest_.price_ == 81L );
104-
cr_assert( sptr->comp_[0].agg_.price_ == 42L );
105-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 2 );
106-
cr_assert( sptr->agg_.pub_slot_ == 3 );
107-
cr_assert( sptr->valid_slot_ == 1 );
108-
109-
// next price doesnt change but slot does
110-
cvar.slot_ = 4;
111-
idata.pub_slot_ = 3;
112-
cr_assert( SUCCESSFULLY_UPDATED_AGGREGATE == dispatch( &prm, acc ) );
113-
cr_assert( sptr->comp_[0].latest_.price_ == 81L );
114-
cr_assert( sptr->comp_[0].agg_.price_ == 81L );
115-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 3 );
116-
cr_assert( sptr->agg_.pub_slot_ == 4 );
117-
cr_assert( sptr->valid_slot_ == 3 );
118-
119-
// next price doesnt change and neither does aggregate but slot does
120-
idata.pub_slot_ = 4;
121-
cvar.slot_ = 5;
122-
cr_assert( SUCCESSFULLY_UPDATED_AGGREGATE == dispatch( &prm, acc ) );
123-
cr_assert( sptr->comp_[0].latest_.price_ == 81L );
124-
cr_assert( sptr->comp_[0].agg_.price_ == 81L );
125-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 4 );
126-
cr_assert( sptr->agg_.pub_slot_ == 5 );
127-
cr_assert( sptr->valid_slot_ == 4 );
128-
129-
// try to publish back-in-time
130-
idata.pub_slot_ = 1;
131-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
132-
133-
134-
// Publishing a wide CI results in a status of unknown.
135-
136-
// check that someone doesn't accidentally break the test.
137-
cr_assert(sptr->comp_[0].latest_.status_ == PC_STATUS_TRADING);
138-
idata.pub_slot_ = 5;
139-
cvar.slot_ = 6;
140-
idata.price_ = 50;
141-
idata.conf_ = 6;
142-
cr_assert( SUCCESSFULLY_UPDATED_AGGREGATE == dispatch( &prm, acc ) );
143-
cr_assert( sptr->comp_[0].latest_.price_ == 50L );
144-
cr_assert( sptr->comp_[0].latest_.conf_ == 6L );
145-
cr_assert( sptr->comp_[0].latest_.status_ == PC_STATUS_UNKNOWN );
146-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 5 );
147-
cr_assert( sptr->agg_.pub_slot_ == 6 );
148-
// Aggregate is still trading because it uses price from previous slot
149-
cr_assert( sptr->agg_.status_ == PC_STATUS_TRADING );
150-
151-
// Crank one more time and aggregate should be unknown
152-
idata.pub_slot_ = 6;
153-
cvar.slot_ = 7;
154-
cr_assert( SUCCESS == dispatch( &prm, acc ) );
155-
cr_assert( sptr->agg_.status_ == PC_STATUS_UNKNOWN );
156-
}
157-
158-
Test( oracle, upd_price_no_fail_on_error ) {
159-
cmd_upd_price_t idata = {
160-
.ver_ = PC_VERSION,
161-
.cmd_ = e_cmd_upd_price_no_fail_on_error,
162-
.status_ = PC_STATUS_TRADING,
163-
.price_ = 42L,
164-
.conf_ = 9L,
165-
.pub_slot_ = 1
166-
};
167-
SolPubkey p_id = {.x = { 0xff, }};
168-
SolPubkey pkey = {.x = { 1, }};
169-
SolPubkey skey = {.x = { 3, }};
170-
sysvar_clock_t cvar = {
171-
.slot_ = 1
172-
};
173-
uint64_t pqty = 100, sqty = 200;
174-
pc_price_t sptr[1];
175-
sol_memset( sptr, 0, sizeof( pc_price_t ) );
176-
sptr->magic_ = PC_MAGIC;
177-
sptr->ver_ = PC_VERSION;
178-
sptr->ptype_ = PC_PTYPE_PRICE;
179-
sptr->type_ = PC_ACCTYPE_PRICE;
180-
sptr->num_ = 1;
181-
182-
SolAccountInfo acc[] = {{
183-
.key = &pkey,
184-
.lamports = &pqty,
185-
.data_len = 0,
186-
.data = NULL,
187-
.owner = NULL,
188-
.rent_epoch = 0,
189-
.is_signer = true,
190-
.is_writable = true,
191-
.executable = false
192-
},{
193-
.key = &skey,
194-
.lamports = &PRICE_ACCOUNT_LAMPORTS,
195-
.data_len = sizeof( pc_price_t ),
196-
.data = (uint8_t*)sptr,
197-
.owner = &p_id,
198-
.rent_epoch = 0,
199-
.is_signer = false,
200-
.is_writable = true,
201-
.executable = false
202-
},{
203-
.key = (SolPubkey*)sysvar_clock,
204-
.lamports = &sqty,
205-
.data_len = sizeof( sysvar_clock_t ),
206-
.data = (uint8_t*)&cvar,
207-
.owner = &p_id,
208-
.rent_epoch = 0,
209-
.is_signer = false,
210-
.is_writable = false,
211-
.executable = false
212-
}};
213-
SolParameters prm = {
214-
.ka = acc,
215-
.ka_num = 3,
216-
.data = (const uint8_t*)&idata,
217-
.data_len = sizeof( idata ),
218-
.program_id = &p_id
219-
};
220-
221-
// We haven't permissioned the publish account for the price account
222-
// yet, so any update should fail silently and have no effect. The
223-
// transaction should "succeed".
224-
cr_assert( SUCCESS == dispatch( &prm, acc ) );
225-
cr_assert( sptr->comp_[0].latest_.price_ == 0L );
226-
cr_assert( sptr->comp_[0].latest_.conf_ == 0L );
227-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 0 );
228-
cr_assert( sptr->agg_.pub_slot_ == 0 );
229-
cr_assert( sptr->valid_slot_ == 0 );
230-
231-
// Now permission the publish account for the price account.
232-
pc_pub_key_assign( &sptr->comp_[0].pub_, (pc_pub_key_t*)&pkey );
233-
234-
// The update should now succeed, and have an effect.
235-
cr_assert( SUCCESS == dispatch( &prm, acc ) );
236-
cr_assert( sptr->comp_[0].latest_.price_ == 42L );
237-
cr_assert( sptr->comp_[0].latest_.conf_ == 9L );
238-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 1 );
239-
cr_assert( sptr->agg_.pub_slot_ == 1 );
240-
cr_assert( sptr->valid_slot_ == 0 );
241-
242-
// Invalid updates, such as publishing an update for the current slot,
243-
// should still fail silently and have no effect.
244-
idata.price_ = 55L;
245-
idata.conf_ = 22L;
246-
idata.pub_slot_ = 1;
247-
cr_assert( SUCCESS == dispatch( &prm, acc ) );
248-
cr_assert( sptr->comp_[0].latest_.price_ == 42L );
249-
cr_assert( sptr->comp_[0].latest_.conf_ == 9L );
250-
cr_assert( sptr->comp_[0].latest_.pub_slot_ == 1 );
251-
cr_assert( sptr->agg_.pub_slot_ == 1 );
252-
cr_assert( sptr->valid_slot_ == 0 );
253-
}
254-
255-
Test( oracle, upd_aggregate ) {
256-
pc_price_t px[1];
257-
sol_memset( px, 0, sizeof( pc_price_t ) );
258-
pc_price_info_t p1 = { .price_=100, .conf_=10,
259-
.status_ = PC_STATUS_TRADING, .corp_act_status_= 0, .pub_slot_ = 1000 };
260-
pc_price_info_t p2 = { .price_=200, .conf_=20,
261-
.status_ = PC_STATUS_TRADING, .corp_act_status_= 0, .pub_slot_ = 1000 };
262-
pc_price_info_t p3 = { .price_=300, .conf_=30,
263-
.status_ = PC_STATUS_TRADING, .corp_act_status_= 0, .pub_slot_ = 1000 };
264-
pc_price_info_t p4 = { .price_=400, .conf_=40,
265-
.status_ = PC_STATUS_TRADING, .corp_act_status_= 0, .pub_slot_ = 1000 };
266-
267-
// single publisher
268-
px->num_ = 1;
269-
px->last_slot_ = 1000;
270-
px->agg_.pub_slot_ = 1000;
271-
px->comp_[0].latest_ = p1;
272-
upd_aggregate( px, 1001, 1 );
273-
cr_assert( px->agg_.price_ == 100 );
274-
cr_assert( px->agg_.conf_ == 10 );
275-
cr_assert( px->twap_.val_ == 100 );
276-
cr_assert( px->twac_.val_ == 10 );
277-
cr_assert( px->num_qt_ == 1 );
278-
cr_assert( px->timestamp_ == 1 );
279-
cr_assert( px->prev_slot_ == 0 );
280-
cr_assert( px->prev_price_ == 0 );
281-
cr_assert( px->prev_conf_ == 0 );
282-
cr_assert( px->prev_timestamp_ == 0 );
283-
284-
// two publishers
285-
px->num_ = 0;
286-
px->last_slot_ = 1000;
287-
px->agg_.pub_slot_ = 1000;
288-
px->comp_[px->num_++].latest_ = p2;
289-
px->comp_[px->num_++].latest_ = p1;
290-
upd_aggregate( px, 1001, 2 );
291-
cr_assert( px->agg_.price_ == 145 );
292-
cr_assert( px->agg_.conf_ == 55 );
293-
cr_assert( px->twap_.val_ == 106 );
294-
cr_assert( px->twac_.val_ == 16 );
295-
cr_assert( px->num_qt_ == 2 );
296-
cr_assert( px->timestamp_ == 2 );
297-
cr_assert( px->prev_slot_ == 1000 );
298-
cr_assert( px->prev_price_ == 100 );
299-
cr_assert( px->prev_conf_ == 10 );
300-
cr_assert( px->prev_timestamp_ == 1 );
301-
302-
// three publishers
303-
px->num_ = 0;
304-
px->last_slot_ = 1000;
305-
px->agg_.pub_slot_ = 1000;
306-
px->comp_[px->num_++].latest_ = p2;
307-
px->comp_[px->num_++].latest_ = p1;
308-
px->comp_[px->num_++].latest_ = p3;
309-
upd_aggregate( px, 1001, 3 );
310-
cr_assert( px->agg_.price_ == 200 );
311-
cr_assert( px->agg_.conf_ == 90 );
312-
cr_assert( px->twap_.val_ == 114 );
313-
cr_assert( px->twac_.val_ == 23 );
314-
cr_assert( px->num_qt_ == 3 );
315-
cr_assert( px->timestamp_ == 3 );
316-
cr_assert( px->prev_slot_ == 1000 );
317-
cr_assert( px->prev_price_ == 145 );
318-
cr_assert( px->prev_conf_ == 55 );
319-
cr_assert( px->prev_timestamp_ == 2 );
320-
321-
// four publishers
322-
px->num_ = 0;
323-
px->last_slot_ = 1000;
324-
px->agg_.pub_slot_ = 1000;
325-
px->comp_[px->num_++].latest_ = p3;
326-
px->comp_[px->num_++].latest_ = p1;
327-
px->comp_[px->num_++].latest_ = p4;
328-
px->comp_[px->num_++].latest_ = p2;
329-
upd_aggregate( px, 1001, 4 );
330-
cr_assert( px->agg_.price_ == 245 );
331-
cr_assert( px->agg_.conf_ == 85 );
332-
cr_assert( px->twap_.val_ == 125 );
333-
cr_assert( px->twac_.val_ == 28 );
334-
cr_assert( px->last_slot_ == 1001 );
335-
cr_assert( px->num_qt_ == 4 );
336-
cr_assert( px->timestamp_ == 4 );
337-
cr_assert( px->prev_slot_ == 1000 );
338-
cr_assert( px->prev_price_ == 200 );
339-
cr_assert( px->prev_conf_ == 90 );
340-
cr_assert( px->prev_timestamp_ == 3 );
341-
342-
upd_aggregate( px, 1025, 5 );
343-
cr_assert( px->agg_.status_ == PC_STATUS_TRADING );
344-
cr_assert( px->last_slot_ == 1025 );
345-
cr_assert( px->num_qt_ == 4 );
346-
cr_assert( px->timestamp_ == 5 );
347-
cr_assert( px->prev_slot_ == 1001 );
348-
cr_assert( px->prev_price_ == 245 );
349-
cr_assert( px->prev_conf_ == 85 );
350-
cr_assert( px->prev_timestamp_ == 4 );
351-
352-
// check what happens when nothing publishes for a while
353-
upd_aggregate( px, 1026, 10 );
354-
cr_assert( px->agg_.status_ == PC_STATUS_UNKNOWN );
355-
cr_assert( px->last_slot_ == 1025 );
356-
cr_assert( px->num_qt_ == 0 );
357-
cr_assert( px->timestamp_ == 10 );
358-
cr_assert( px->prev_slot_ == 1025 );
359-
cr_assert( px->prev_timestamp_ == 5 );
360-
361-
// Check that the prev_* fields don't update if the aggregate status is UNKNOWN
362-
uint64_t prev_slot_ = px->prev_slot_;
363-
int64_t prev_price_ = px->prev_price_;
364-
uint64_t prev_conf_ = px->prev_conf_;
365-
int64_t prev_timestamp_ = px->prev_timestamp_;
366-
upd_aggregate( px, 1028, 12 );
367-
cr_assert( px->agg_.status_ == PC_STATUS_UNKNOWN );
368-
cr_assert( px->timestamp_ == 12 );
369-
cr_assert( px->prev_slot_ == prev_slot_ );
370-
cr_assert( px->prev_price_ == prev_price_ );
371-
cr_assert( px->prev_conf_ == prev_conf_ );
372-
cr_assert( px->prev_timestamp_ == prev_timestamp_ );
373-
}

program/rust/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub enum OracleError {
2828
InvalidSystemAccount = 606,
2929
#[error("InvalidWritableAccount")]
3030
InvalidWritableAccount = 607,
31-
#[error("InvalidWritableAccount")]
31+
#[error("InvalidFreshAccount")]
3232
InvalidFreshAccount = 608,
3333
}
3434

0 commit comments

Comments
 (0)