Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delivery Utils #7

Draft
wants to merge 4 commits into
base: feat/timeout
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
pragma solidity ^0.8.0;
import {Bid, FeesData} from "../../../../common/Structs.sol";
import {IAuctionContract} from "../../../../interfaces/IAuctionContract.sol";


/// @title AuctionHouse
/// @notice Contract for managing auctions and placing bids
contract FirstBidAuction is IAuctionContract {
Expand Down
12 changes: 11 additions & 1 deletion contracts/apps/payload-delivery/app-gateway/AuctionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) {

auctionStarted[asyncId_] = true;
emit AuctionStarted(asyncId_);

// todo: fix auction contract address
uint256 auctionEndDelaySeconds = IAuctionContract(address(this))
.auctionEndDelaySeconds();

watcherPrecompile().setTimeout(
abi.encodeWithSelector(this.endAuction.selector, asyncId_),
auctionEndDelaySeconds
Expand All @@ -57,8 +60,9 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) {
) external {
require(!auctionClosed[asyncId_], "Auction closed");

// todo: check chain slug for multiple offchain vm
address transmitter = signatureVerifier__.recoverSigner(
keccak256(abi.encode(address(this), asyncId_, fee)),
keccak256(abi.encode(address(this), asyncId_, fee, extraData)),
transmitterSignature
);

Expand All @@ -67,16 +71,22 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) {
transmitter: transmitter,
extraData: extraData
});

// todo: fix auction contract address
(address auctionContract, FeesData memory feesData) = AuctionHouse()
.getAuctionContractAndFeesData(asyncId_);

require(fee <= feesData.maxFees, "Bid exceeds max fees");

// todo: revisit if should revert or not
require(
IAuctionContract(auctionContract).isNewBidBetter(
winningBids[asyncId_],
newBid
),
"Bid is not better"
);

winningBids[asyncId_] = newBid;
emit BidPlaced(asyncId_, newBid);
}
Expand Down
72 changes: 20 additions & 52 deletions contracts/apps/payload-delivery/app-gateway/BatchAsync.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@ abstract contract BatchAsync is QueueAsync {
PayloadDetails[]
memory payloadDetailsArray = createPayloadDetailsArray();

// Check if batch contains only READ calls
if (_isReadOnlyBatch(payloadDetailsArray)) {
return _processReadOnlyBatch(payloadDetailsArray);
}

// Default flow for other cases (including mixed read/write)
return
deliverPayload(payloadDetailsArray, feesData_, auctionEndDelayMS_);
Expand All @@ -75,7 +70,7 @@ abstract contract BatchAsync is QueueAsync {
function deliverPayload(
PayloadDetails[] memory payloadDetails_,
FeesData memory feesData_,
uint256 auctionEndDelayMS_
uint256 auctionEndDelay_
) internal returns (bytes32) {
address forwarderAppGateway = msg.sender;
bytes32 asyncId = getCurrentAsyncId();
Expand Down Expand Up @@ -122,18 +117,24 @@ abstract contract BatchAsync is QueueAsync {
);
}

if (readEndIndex == payloadDetails_.length) {
return asyncId;
}

// Process remaining payloads normally
for (uint256 i = readEndIndex; i < payloadDetails_.length; i++) {
if (payloadDetails_[i].payload.length > 24.5 * 1024)
revert PayloadTooLarge();

// Rest of the existing deliverPayload logic for each payload
if (payloadDetails_[i].callType == CallType.DEPLOY) {
// considering contract factory as plug of delivery helper
payloadDetails_[i].target = getPlugAddress(
address(this),
payloadDetails_[i].chainSlug
);
} else if (payloadDetails_[i].callType == CallType.WRITE) {
// todo: if need to remove this
forwarderAppGateway = IAddressResolver(addressResolver)
.contractsToGateways(msg.sender);

Expand All @@ -149,26 +150,31 @@ abstract contract BatchAsync is QueueAsync {
this.callback.selector,
abi.encode(asyncId)
);
payloadDetailsArrays[asyncId].push(payloadDetails_[i]);

payloadBatchDetails[asyncId].push(payloadDetails_[i]);
}

totalPayloadsRemaining[asyncId] = payloadDetails_.length - readEndIndex;
payloadBatches[asyncId] = PayloadBatch({
appGateway: forwarderAppGateway,
feesData: feesData_,
currentPayloadIndex: 0,
auctionEndDelayMS: auctionEndDelayMS_,
isBatchCancelled: false
currentPayloadIndex: readEndIndex,
auctionEndDelaySeconds: auctionEndDelay_,
winningBid: Bid({
fee: 0,
transmitter: address(0),
extraData: new bytes(0)
}),
isBatchCancelled: false,
totalPayloadsRemaining: payloadDetails_.length - readEndIndex
});

IAuctionManager(auctionManager).startAuction(asyncId);

emit PayloadSubmitted(
asyncId,
forwarderAppGateway,
payloadDetails_,
feesData_,
auctionEndDelayMS_
auctionEndDelay_
);
return asyncId;
}
Expand Down Expand Up @@ -207,45 +213,7 @@ abstract contract BatchAsync is QueueAsync {
bytes32 asyncId_,
uint256 index_
) external view returns (PayloadDetails memory) {
return payloadDetailsArrays[asyncId_][index_];
}

function _isReadOnlyBatch(
PayloadDetails[] memory payloadDetails_
) internal pure returns (bool) {
for (uint256 i = 0; i < payloadDetails_.length; i++) {
if (payloadDetails_[i].callType != CallType.READ) {
return false;
}
}
return true;
}

function _processReadOnlyBatch(
PayloadDetails[] memory payloadDetails_
) internal returns (bytes32) {
bytes32 asyncId = getCurrentAsyncId();
asyncCounter++;

// Process all reads in a loop without waiting for callbacks
for (uint256 i = 0; i < payloadDetails_.length; i++) {
bytes32 payloadId = watcherPrecompile().query(
payloadDetails_[i].chainSlug,
payloadDetails_[i].target,
payloadDetails_[i].next,
payloadDetails_[i].payload
);
payloadIdToBatchHash[payloadId] = asyncId;

emit PayloadAsyncRequested(
asyncId,
payloadId,
bytes32(0), // No root for read-only queries
payloadDetails_[i]
);
}

return asyncId;
return payloadBatchDetails[asyncId_][index_];
}

/// @notice Withdraws funds to a specified receiver
Expand Down
117 changes: 93 additions & 24 deletions contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) {
payloadDetails_
);

PayloadDetails storage payloadDetails = payloadDetailsArrays[
PayloadDetails storage payloadDetails = payloadBatchDetails[
asyncId
][payloadBatch.currentPayloadIndex];

Expand All @@ -69,35 +69,104 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) {
/// @param asyncId_ The ID of the batch
function _finalizeNextPayload(bytes32 asyncId_) internal {
PayloadBatch storage payloadBatch = payloadBatches[asyncId_];
uint256 currentPayloadIndex = payloadBatch.currentPayloadIndex;
totalPayloadsRemaining[asyncId_]--;
uint256 currentIndex = payloadBatch.currentPayloadIndex;
PayloadDetails[] storage payloads = payloadBatchDetails[asyncId_];

PayloadDetails[] storage payloads = payloadDetailsArrays[asyncId_];
// Find consecutive reads
if (payloads[currentIndex].callType == CallType.READ) {
uint256 readEndIndex = currentIndex;
while (
readEndIndex + 1 < payloads.length &&
payloads[readEndIndex + 1].callType == CallType.READ
) {
readEndIndex++;
}

PayloadDetails storage payloadDetails = payloads[currentPayloadIndex];
// Process all reads together
address batchPromise = IAddressResolver(addressResolver)
.deployAsyncPromiseContract(address(this));
isValidPromise[batchPromise] = true;

bytes32 payloadId;
bytes32 root;
for (uint256 i = currentIndex; i <= readEndIndex; i++) {
bytes32 payloadId = watcherPrecompile().query(
payloads[i].chainSlug,
payloads[i].target,
[batchPromise, address(0)],
payloads[i].payload
);
payloadIdToBatchHash[payloadId] = asyncId_;
emit PayloadAsyncRequested(
asyncId_,
payloadId,
bytes32(0),
payloads[i]
);
}

// todo: if multiple query, process all at once
if (payloadDetails.callType == CallType.READ) {
payloadId = watcherPrecompile().query(
payloadDetails.chainSlug,
payloadDetails.target,
payloadDetails.next,
payloadDetails.payload
);
payloadIdToBatchHash[payloadId] = asyncId_;
} else {
FinalizeParams memory finalizeParams = FinalizeParams({
payloadDetails: payloadDetails,
transmitter: winningBids[asyncId_].transmitter
});
totalPayloadsRemaining[asyncId_] -= (readEndIndex -
currentIndex +
1);
payloadBatch.currentPayloadIndex = readEndIndex + 1;
return;
}

// Find consecutive parallel non-read calls
if (!payloads[currentIndex].isSequential) {
uint256 parallelEndIndex = currentIndex;
while (
parallelEndIndex + 1 < payloads.length &&
!payloads[parallelEndIndex + 1].isSequential
) {
parallelEndIndex++;
}

// Process all parallel calls together
address parallelPromise = IAddressResolver(addressResolver)
.deployAsyncPromiseContract(address(this));
isValidPromise[parallelPromise] = true;

for (uint256 i = currentIndex; i <= parallelEndIndex; i++) {
FinalizeParams memory finalizeParams = FinalizeParams({
payloadDetails: payloads[i],
transmitter: winningBids[asyncId_].transmitter
});

(payloadId, root) = watcherPrecompile().finalize(finalizeParams);
payloadIdToBatchHash[payloadId] = asyncId_;
(bytes32 payloadId, bytes32 root) = watcherPrecompile()
.finalize(finalizeParams);
payloadIdToBatchHash[payloadId] = asyncId_;
emit PayloadAsyncRequested(
asyncId_,
payloadId,
root,
payloads[i]
);
}

totalPayloadsRemaining[asyncId_] -= (parallelEndIndex -
currentIndex +
1);
payloadBatch.currentPayloadIndex = parallelEndIndex + 1;
return;
}

emit PayloadAsyncRequested(asyncId_, payloadId, root, payloadDetails);
// Process single sequential call
FinalizeParams memory finalizeParams = FinalizeParams({
payloadDetails: payloads[currentIndex],
transmitter: winningBids[asyncId_].transmitter
});

(bytes32 payloadId, bytes32 root) = watcherPrecompile().finalize(
finalizeParams
);
payloadIdToBatchHash[payloadId] = asyncId_;
emit PayloadAsyncRequested(
asyncId_,
payloadId,
root,
payloads[currentIndex]
);

totalPayloadsRemaining[asyncId_]--;
payloadBatch.currentPayloadIndex++;
}
}
14 changes: 9 additions & 5 deletions contracts/apps/payload-delivery/app-gateway/QueueAsync.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@ abstract contract QueueAsync is AddressResolverUtil {

CallParams[] public callParamsArray;
mapping(address => bool) public isValidPromise;
mapping(bytes32 => Bid) public winningBids;

// payloadId => asyncId
mapping(bytes32 => bytes32) public payloadIdToBatchHash;
// asyncId => PayloadBatch
mapping(bytes32 => PayloadBatch) public payloadBatches;
// asyncId => totalPayloadsRemaining
mapping(bytes32 => uint256) public totalPayloadsRemaining;

// todo: merge all async id vars in one struct
// asyncId => PayloadDetails[]
mapping(bytes32 => PayloadDetails[]) public payloadDetailsArrays;
mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails;

error InvalidPromise();

Expand Down Expand Up @@ -132,7 +131,11 @@ abstract contract QueueAsync is AddressResolverUtil {
bytes32 salt = keccak256(
abi.encode(msg.sender, params.chainSlug, saltCounter++)
);
payload = abi.encode(params.payload, salt);
payload = abi.encodeWithSelector(
IContractFactoryPlug.deployContract.selector,
params.payload,
salt
);
}

return
Expand Down Expand Up @@ -160,6 +163,7 @@ abstract contract QueueAsync is AddressResolverUtil {
(uint32 chainSlug, bytes32 contractId, address appDeployer) = abi
.decode(data_, (uint32, bytes32, address));

// todo: use getOrDeploy here
address forwarderContractAddress = addressResolver
.deployForwarderContract(
appDeployer,
Expand Down
2 changes: 2 additions & 0 deletions contracts/common/Structs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct PayloadBatch {
FeesData feesData;
uint256 currentPayloadIndex;
uint256 auctionEndDelaySeconds;
uint256 totalPayloadsRemaining;
Bid winningBid;
bool isBatchCancelled;
}

Expand Down
3 changes: 3 additions & 0 deletions contracts/socket/Socket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ contract Socket is SocketBase {
*/
error LowGasLimit();
error InvalidSlug();
error InvalidSwitchboard();

////////////////////////////////////////////////////////////
////////////////////// State Vars //////////////////////////
Expand Down Expand Up @@ -83,6 +84,8 @@ contract Socket is SocketBase {
address switchboard = _decodeSwitchboard(payloadId_);
uint32 localSlug = _decodeChainSlug(payloadId_);

(, address configSwitchboard) = _plugConfigs[target_];
if (switchboard != configSwitchboard) revert InvalidSwitchboard();
if (localSlug != chainSlug) revert InvalidSlug();

address transmitter = signatureVerifier__.recoverSigner(
Expand Down