@@ -70,11 +70,12 @@ QPI::sint64 QPI::QpiContextProcedureCall::__qpiQueryOracle(
7070 }
7171#endif
7272
73- // notify about error (status and queryId are 0 , indicating that an error happened before sending query)
73+ // notify about error (status is 0 and queryId is -1 , indicating that an error happened before sending query)
7474 auto * state = (ContractStateType*)contractStates[contractIndex];
7575 auto * input = (QPI::OracleNotificationInput<OracleInterface>*)__qpiAllocLocals (sizeof (QPI::OracleNotificationInput<OracleInterface>));
7676 input->status = ORACLE_QUERY_STATUS_UNKNOWN;
7777 input->queryId = -1 ;
78+ input->subscriptionId = -1 ;
7879 QPI::NoData output;
7980 auto * locals = (LocalsType*)__qpiAllocLocals (sizeof (LocalsType));
8081 notificationProcPtr (*this , *state, *input, output, *locals);
@@ -87,22 +88,114 @@ template <typename OracleInterface, typename ContractStateType, typename LocalsT
8788inline QPI::sint32 QPI::QpiContextProcedureCall::__qpiSubscribeOracle (
8889 const OracleInterface::OracleQuery& query,
8990 void (*notificationProcPtr)(const QPI::QpiContextProcedureCall& qpi, ContractStateType& state, OracleNotificationInput<OracleInterface>& input, NoData& output, LocalsType& locals),
90- QPI::uint32 notificationIntervalInMilliseconds,
9191 unsigned int notificationProcId,
92+ QPI::uint32 notificationPeriodInMilliseconds,
9293 bool notifyWithPreviousReply
9394) const
9495{
96+ // check that query has timestamp member
9597 static_assert (sizeof (query.timestamp ) == sizeof (DateAndTime));
96- // TODO
98+
99+ // check that size of oracle query, oracle reply, notification procedure locals are valid
100+ static_assert (sizeof (OracleInterface::OracleQuery) <= MAX_ORACLE_QUERY_SIZE);
101+ static_assert (sizeof (OracleInterface::OracleReply) <= MAX_ORACLE_REPLY_SIZE);
102+ static_assert (sizeof (LocalsType) <= MAX_SIZE_OF_CONTRACT_LOCALS);
103+
104+ // check that oracle interface and notification input type are as expected
105+ static_assert (sizeof (QPI::OracleNotificationInput<typename OracleInterface::OracleReply>) == 16 + sizeof (OracleInterface::OracleReply));
106+ static_assert (OracleInterface::oracleInterfaceIndex < OI::oracleInterfacesCount);
107+ static_assert (OI::oracleInterfaces[OracleInterface::oracleInterfaceIndex].replySize == sizeof (OracleInterface::OracleReply));
108+ static_assert (OI::oracleInterfaces[OracleInterface::oracleInterfaceIndex].querySize == sizeof (OracleInterface::OracleQuery));
109+
110+ // check contract index
111+ ASSERT (this ->_currentContractIndex < 0xffff );
112+ const QPI::uint16 contractIndex = static_cast <QPI::uint16>(this ->_currentContractIndex );
113+
114+ // check callback
115+ if (!notificationProcPtr || ContractStateType::__contract_index != contractIndex)
116+ return -1 ;
117+
118+ // check vs registry of user procedures for notification
119+ const UserProcedureRegistry::UserProcedureData* procData;
120+ if (!userProcedureRegistry || !(procData = userProcedureRegistry->get (notificationProcId)) || procData->procedure != (USER_PROCEDURE)notificationProcPtr)
121+ return -1 ;
122+ ASSERT (procData->inputSize == sizeof (OracleNotificationInput<OracleInterface>));
123+ ASSERT (procData->localsSize == sizeof (LocalsType));
124+
125+ // get and destroy fee (not adding to contracts execution fee reserve)
126+ const sint64 fee = OracleInterface::getSubscriptionFee (query, notificationPeriodInMilliseconds);
127+ const int contractSpectrumIdx = ::spectrumIndex (this ->_currentContractId );
128+ if (fee >= 0 && contractSpectrumIdx >= 0 && decreaseEnergy (contractSpectrumIdx, fee))
129+ {
130+ // log burning of QU
131+ const QuTransfer quTransfer = { this ->_currentContractId , m256i::zero (), fee };
132+ logger.logQuTransfer (quTransfer);
133+
134+ // try to subscribe
135+ QPI::sint32 subscriptionId = oracleEngine.startContractSubscription (
136+ contractIndex, OracleInterface::oracleInterfaceIndex,
137+ &query, sizeof (query), notificationPeriodInMilliseconds,
138+ notificationProcId, offsetof (OracleInterface::OracleQuery, timestamp));
139+ if (subscriptionId >= 0 )
140+ {
141+ // success
142+ if (notifyWithPreviousReply)
143+ {
144+ // notify contract with previous reply if any is available
145+ const OracleSubscription* subscription = oracleEngine.getOracleSubscription (subscriptionId);
146+ if (subscription && subscription->lastRevealedQueryId >= 0 )
147+ {
148+ const int64_t queryId = subscription->lastRevealedQueryId ;
149+ auto * state = (ContractStateType*)contractStates[contractIndex];
150+ auto * input = (QPI::OracleNotificationInput<OracleInterface>*)__qpiAllocLocals (sizeof (QPI::OracleNotificationInput<OracleInterface>));
151+ input->status = ORACLE_QUERY_STATUS_SUCCESS;
152+ input->queryId = queryId;
153+ input->subscriptionId = subscriptionId;
154+ oracleEngine.getOracleReply (queryId, &input->reply , sizeof (input->reply ));
155+ QPI::NoData output;
156+ auto * locals = (LocalsType*)__qpiAllocLocals (sizeof (LocalsType));
157+ notificationProcPtr (*this , *state, *input, output, *locals);
158+ __qpiFreeLocals ();
159+ __qpiFreeLocals ();
160+ }
161+ }
162+
163+ return subscriptionId;
164+ }
165+ else if (fee > 0 )
166+ {
167+ // failure -> refund fee
168+ oracleEngine.refundFees (_currentContractId, fee);
169+ }
170+ }
171+ #if !defined(NDEBUG) && !defined(NO_UEFI)
172+ else
173+ {
174+ addDebugMessage (L" Cannot start contract oracle subscription due to fee issue!" );
175+ }
176+ #endif
177+
178+ // notify about error (status is 0 and subscriptionId is -1, indicating that subscribing failed)
179+ auto * state = (ContractStateType*)contractStates[contractIndex];
180+ auto * input = (QPI::OracleNotificationInput<OracleInterface>*)__qpiAllocLocals (sizeof (QPI::OracleNotificationInput<OracleInterface>));
181+ input->status = ORACLE_QUERY_STATUS_UNKNOWN;
182+ input->queryId = -1 ;
183+ input->subscriptionId = -1 ;
184+ QPI::NoData output;
185+ auto * locals = (LocalsType*)__qpiAllocLocals (sizeof (LocalsType));
186+ notificationProcPtr (*this , *state, *input, output, *locals);
187+ __qpiFreeLocals ();
188+ __qpiFreeLocals ();
97189 return -1 ;
98190}
99191
100192inline bool QPI::QpiContextProcedureCall::unsubscribeOracle (
101193 QPI::sint32 oracleSubscriptionId
102194) const
103195{
104- // TODO
105- return false ;
196+ ASSERT (this ->_currentContractIndex < 0xffff );
197+ const QPI::uint16 contractIndex = static_cast <QPI::uint16>(this ->_currentContractIndex );
198+ return oracleEngine.stopContractSubscription (oracleSubscriptionId, contractIndex);
106199}
107200
108201template <typename OracleInterface>
0 commit comments