Git Product home page Git Product logo

socket-dl's People

Contributors

ameeshaagrawal avatar arthcp avatar kanthgithub avatar madhur4444 avatar rugamoto avatar shreykeny avatar themaskedman981 avatar vaibhavchellani avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

socket-dl's Issues

Test Oracle

  • Test role management on oracle for setRelativeGasPrice and setTransmitManager
  • Both success and failure cases

Check contracts while registering/connecting

  • Need to make sure contracts taken as input on socket follow proper interface.
    • Plug and Switchboard can enter the system as public inputs
    • Need to add explicit interface checks when these are added.
  • Some switchboards only work for specific path.
    • Eg. NativePolygon switchboard would work only between Ethereum and Polygon.
    • But these can be registered for other paths since register function is open to public.
    • One approach is to enforce switchboard itself to send register call, take its address from msg.sender
    • And have these checks at switchboard level before call comes to socket.

Post reorg cleanup

Lots of messy stuff pushed during the recent reorg, needs cleanup

  • Remove all unused code and contracts.
  • Address all todos.

One way trip fuses on switchboards

  • The tripping of fuses on switchboards is toggleable.
  • This creates an attack vector, making the tripping pointless.
  • If one attester/watcher goes rogue, they can propose wrong root and undo fuse trips.
  • Logic needs to be changed to make tripFuse one way.
  • Admin function can be added to reverse mistakes for now.

Native Initiators

  • Create a contracts that can trigger sending roots using native bridges.
  • Function should take packetId as input, read root from socket and initiate relay. Anyone should be able to trigger this.
  • Socket contract logic should also change to store and read sealed roots.
    function initateNativeConfirmation(
        uint256 packetId
    ) external {
        bytes32 root = _socket__.getSealedRoot(packetId);
        bytes memory payload = abi.encode(packetId, root);
        // send payload using native
    }
  • Switchboard on destination chain should receive this payload for root confirmation.
  • Maybe it makes sense to club initiate logic into source switchboard.

Following Initiators should be needed

  • PolygonNativeL1toL2
  • PolygonNativeL2toL1
  • ArbitrumNativeL1toL2
  • ArbitrumNativeL2toL1
  • OptimismNative

Native Switchboards

  • Native switchboards need to receive the confirmation sent by #58
  • The switchboards should follow #42
  • The root received needs to be stored against its packetId.
  • This stored root should be compared in allowPacket function to check if it isvalid.
  • Pay fees function should have specific fee equations, eg have native fees in artbitrum.
  • Following switchboards should be needed
    • PolygonNativeL1toL2
    • PolygonNativeL2toL1
    • ArbitrumNativeL1toL2
    • ArbitrumNativeL2toL1
    • OptimismNative

Rescue functions

Admin controlled functions to rescue erc20 and native token need to be present on all contracts.

oracle for tx.gasprice

The tx.gasprice can be manipulated from outside (worst case with 0 gasprice by flashbots) which affects the fees calculations in Socket.
To prevent this, we should have some external oracle giving correct values.

Capacitor Factoctory Tests

  • Tests for Capacitor factory.
  • Should deploy SingleCapacitor and SingleDecapacitor.
  • Test for Access Conttrol of addPackedMessage and sealPacket on deployed capacitor.

Rearch fee deduction for efficiency

Current flow

  • In current outbound function, fee calculation and collection is fragmented.
  • Min amounts are fetched from TransmitManager, Switchboard and ExecutionManager.
  • Then there is a check to see if received value is enough.
  • After this fees are sent to 3 different places.
  • And these 3 places again have a min fee check.

Reasons for current flow

  • Having fee logic for specif components at their dedicated places makes the logic simple and modular.
  • Each end up having control over upgrades and collection/distribution.
  • Socket doesnt know how much min fees are expected at each component, and so it doenst know how much to send to them. So it needs to fetch all the fees and then distribute.

Possible improvement 1

  • Merge all fee logic into single component.
  • This removes all checks at socket level. It can simply pass all the value to fee collector.
  • Fee collector can check each fee individually and see if min is met.
  • Gas cost is reduced by a lot because only fee collector is accessed from socket and oracle is accessed only once downstream.
  • Makes all the fee logic coupled in single place.

Possible improvement 2

  • Keep 3 components, send all fees to them and have them send back remaining fees to socket.
  • Modular design stays.
  • Some gas savings but not max.

Possible improvement 3

  • Access oracle from socket and pass the values to all components.
  • Couples gas oracle with socket.

Config Management on socket

  • When a verification method needs to be introduced on socket, its corresponding switchboard needs to be registered.

  • Switchboards are specific to sibling chains and have dedicated capacitors deployed while registration.

  • Following function needs to be added to socket for registering switchboards

    function registerSwitchBoard(
        address switchBoardAddress,
        uint256 siblingChainSlug,
        uint256 capacitorType
    ) external {
        require(capacitors[switchBoardAddress] == 0 && decapacitors[switchBoardAddress] == 0);
        (ICapacitor capacitor, IDeCapacitor decapacitor) = capacitorRegistry.deploy(uint256 capacitorType, siblingChainSlug);
        capacitors[switchBoardAddress] = capacitor;
        decapacitors[switchBoardAddress] = decapacitor;
    }
  • Plugs should tell inbound and outbound switchboard address along with sibling chain and address to be connected.

    function connect(
        uint256 siblingChainSlug,
        address siblingPlugAddress,
        address inboundSwitchboard,
        address outboundSwitchboard
    ) external {
        (ICapacitor capacitor, IDeCapacitor decapacitor) = capacitorRegistry.deploy(uint256 capacitorType, siblingChainSlug);
        inboundSwitchboards[msg.sender][siblingChainSlug] = inboundSwitchboard;
        outboundSwitchboards[msg.sender][siblingChainSlug] = outboundSwitchboard;
        siblingPlugs[msg.sender][siblingChainSlug] = siblingPlugAddress;
    }

ExecutionManager review changes

  • Add a setter for gasPriceOracle_
  • change isExecutor to sig based (will take packedMessage and sig and then check if recovered address has executor role)
  • lib for withdraw fees which can be modified later as needed (removes redundant code)

Arb and Opt gas limits

  • The gas limits on arbitrum and optimism change based on L1 gas price.
  • Need to consider this for fee calculation.
  • Admin sets these on TransmitManager and Switchboards.
  • Lets monitor Dalchini and see how often we need to update, maybe build it similar to RelativeGasPrice oracle.

Happy Case end to end test

  • Integration test for Counter.
  • Use fast switchboard, 1 transmitter, 1 watcher.
  • Make Setup.t.sol work for full deploy and configure.
  • Test using zero fees.

optimistic switchboard - Trip function enhancement

logic for source-chain specific trip:

smart contract changes:

contract: OptimisticSwitchboard.sol

  1. update mapping tripSingleFuse to take sourceChainId instead of packetId

  2. rename function tripSingle to tripPathFuse

    • trip path will set the mapping for the sourceChainId to false
  3. in function allowPacket

    • derive the sourceChainId from packetId.
      - packetId is combination of packed sourceChainId and packetId

    • check if the tripSingleFuse mapping has been set to true for the sourceChainId

      • If yes, then return false
      • existing checks still remain same

off-chain changes

  • add new column root_propose
  • rename column root to root_seal
  • on sealEvent update the value for root_seal
  • on proposeEvent update the value for root_propose

Watcher Mode in attester:

  • add a scheduling which keeps checking for:
    • records in packets table which has root_seal and root_propose
    • compare and do tripPathFuse on mismatch (edited)

Relay source plug caller

Summary

Send the address of the caller of source plug as part of message payload or metadata.

Need

Multiple projects provided feedback saying this is a very important feature and its been missed by other bridges.

Approach

  1. Take sender as a new param for outbound, add it to message metadata.
    • Increases param for all plugs for outbound and execute functions.
    • Increases gas cost for all plugs.
    • Assumed to be caller at socket level but no way to verify since it is a param.
  2. Make a template to send it as part of payload.
    • Increases gas cost for only the plugs that need this functionality.
    • No increase in socket level params.
    • Too many templates start making devx worse. (we probably need to figure out template composability)

Note: Sender will need to be bytes with translation support for non EVM support (#92)

Fee collection and Distribution

Goals

  • Attesters and Executors need to be compensated for their transaction fees
  • They also need to be rewarded to keep the protocol secure.

Switchboard interface

Switchboard defines the security settings that a plug can select.
It is the only input that needs to be taken from plugs, apart from sibling chain and address.
Switchboard should also have the fee collection and distribution logic for the defined security.

Socket is expected to call following functions on switchboard.

  • Function to check if packet is valid.
    function allowPacket(bytes32 root) external returns (bool) {}
  • Function to pay fees for verification. The fees needed for execution are included under verification for now.
  • The function is expected to revert if fees are not enough.
    function payFees(uint256 msgGasLimit) external payable {}

Native bridge verification fees

  • Native bridges like arbitrum (also verify optimism) need native fees along with bridge transaction.
  • These fees need to be estimated on chain to verify if plug sent enough fees to cover verification initiation.
  • Dig deeper about how the gas fee is maintained on their contracts.
  • Maybe we can read if from the contract that checks if fees are enough on their end.
  • If not possible, we can try to deduce it using the gas price of destination chain.

fix REVESE_STATUS_TRAVERSED on switchboard

Problem

Under current implementation, if new attestor is added after a packet is confirmed, the packet goes back to being unconfirmed till the new attester sends their confirmation.
This needs to be avoided since a packet going from confirmed to unconfirmed state can break a lot of flows.

Solution

While attesting, mark a flag saying packet is confirmed when enough confirmations are reached.
Check this flag in allow packet.

isPacketConfirmed 

### Further problem
This solution introduces another edge case. 

Create Readme

Readme should have the following information -

  • Steps to setup the project
  • Steps to run tests
  • Steps to deploy
  • Project structure
  • Ways to contribute
  • Code conventions to follow

Socket (move seal and propose in socket)

  • Add seal function to socket.
    • Can be called by anyone
    • Take signature, siblingChainSlug as input (maybe accum address too?)
    • Get latest unsealed root from accum.
    • Check on TransmitManager if signer is allowed.
      function seal(
          uint256 siblingChainSlug,
          address accum, // ??
          bytes signature,
      ) external {
          bytes32 root = accum__.sealPacket();
          
          require(
              transmitManager.checkTransmitter(chainSlug, siblingChainSlug, root, signature),
              "invalid sealer"
          );
      }
  • Add propose function to socket.
    • Can be called by anyone.
    • Take root, signature, siblingChainSlug as input (maybe accum address too?)
    • Check on TransmitManager if signer is allowed.
    • Store root.
      function propose(
          uint256 siblingChainSlug,
          address accum, // ??
          bytes32 root,
          bytes signature,
      ) external {
          require(
              transmitManager.checkTransmitter(chainSlug, siblingChainSlug, root, signature),
              "invalid proposer"
          );
          roots[root] = true;
      }

Onchain gas price oracles

  • Make an oracle to get gas prices of destination chains.
  • These values will be read by TransmitManager and Switchboards to calculate minimum fees for a message.
  • The oracle will be maintained by Transmitters for now.
  • It should have following function to read the gas prices
    function getGasPrice(uint256 chainSlug) external view returns (uint256) {}

Optimistic Switchboard

  • Implement the interface defined in #42
  • Should have a defined timeout after which roots are considered valid.
  • Keep a set of watchers who are allowed to trip the fuse for a packet and stop its execution.
  • Maybe also include a global fuse to stop all execution.
  • Fee calculation can be done under #45

Non EVM chain support (polygon avail)

Socket contracts need to be tested with a non EVM chain to confirm that the system can be extended.
Since we are in touch with Avail team and have to add their support in near future; we want to do the mvp with them.

What does extending to non EVM chain mean?

  • Plugs on EVM chains should be able to connect to non EVM remotes (chainSlug+plugAddress)
  • Same security assumptions should apply to non EVM chain.

What does extending to non EVM chain not mean?

  • Porting same contracts and architecture to new chain.
    It is likely that a different contract architecture makes sense for a different gas environment.

Likely change

  • Only the plug pointers i.e. chainSlug+plugAddress should need to be made generic (bytes)
  • This essentially lets the plugs define their remote counterparts while connecting.
  • Might need dedicated switchboards based on how verification happens.
  • Might need custom hasher and signature verifiers if these are not available on new chain.
    • hasher and signature verifiers can be upgraded to use different function based on src/dst chainSlugs.

GasPriceOracle review changes

  • modify nonces to nonceLastUsed which will store nonce last used for given address
  • Add updatedAt to setSourceGasPrice too
  • rename: SignatureAlreadyUsed => NonceAlreadyUsed, GasPriceUpdated => RelativeGasPriceUpdated

Upgradebility of socket components

Socket DL is being build with an approach of giving integrators choice over any component/lib upgradation.
All parts need to be aligned with the ethos. Especially the ones directly in Socket contract like TransmitManager, Hasher etc.

Capacitor Factory

  • Capacitors need to be deployed for each new Switchboard.
  • A Factory should be created that deploys the capacitors based on given type and returns the addresses.
  • Socket is expected to call this Factory each time new Switchboard is being registered.
  • All capacitors, decapacitors should follow the same interface so that socket can interact with them.
  • New Capacitor types can be introduced in future by changing CapacitorFactory.
  • Factory should be able to deploy different capacitors based on specified sibling chain.
  • Decapacitors can be reused since they are stateless. This can be done in later iteration.
    function deploy(
        uint256 type,
        uint256 siblingChainSlug
    ) external returns (ICapacitor, IDecapacitor) {
        if (type == 1) {
            return (new SingleCapacitor(msg.sender), new SingleDecapacitor());
        }
        if (type == 2) {
            return (new HashChainCapacitor(msg.sender), new HashChainDecapacitor());
        }
        if (type == 1) {
            return (new MerkleCapacitor(msg.sender), new MerkleDecapacitor());
        }
    }

Execution fees, retry and refuel

Goals

  • Executors need to be rewarded for execution of messages.
  • There needs to be a market for priority execution.
  • Executors should optimize for successful message execution and leave retry room in case failure.
  • We should be able to move fee claim fully onchain in future iterations.
  • Plugs should be able to send native tokens along with message.

Summary from discussion

  • Executor fee accounting

    • Priority market can only be established if executors earn more reward for picking high fee txs.
    • Executor fee accounting needs to happen on chain to enable distribution.
    • Storing message specific data becomes uniterateable at scale.
    • Need to work with totals while accounting for message specific fee differences.
    • If we send message specific fees as part of message metadata, it can be used to calculate executor's total reward.
      // dst socket
      feesEarned[srcChainId][switchboard][executor] += messageFees;
    • This total can be relay to source switchboard while claiming.
       // src switchboard
       payout = feesEarned - claimedAmount;
  • Healthy message delivery.

    • Messages are expected to be time sensitive and sometimes fail until outside conditions are met.
    • Execution of failing messages needs to be discouraged.
    • Retry till successful execution needs to be encouraged.
    • Going with simple logic of no reward in fail and full fees on execution success.
    • Allow retries till success. (note: attack vector of executing very stale failed messages exists)
  • Refund

    • Refund of fees acts against the mechanism to establish priority market.
    • There can be mechanisms that involve an upper limit on priority fee but feels like unneeded complexity at base protocol.
    • So no refunds once fees are paid.
  • Send value along with message.

    • There can be usecases where native tokens need to be sent along with message.
    • This additional amount needs to be accepted on source.
    • The executor on destination chain can then send the required native tokens while executing and claim the collected tokens on source chain after successful execution.
    • There can be cases where native tokens on both chains are different and need to be converted.
    • For this we can reuse the oracle on source used for fee estimation.
    • Both original and converted amount need to be added to message metadata. (explore ways to avoid adding both)
    • converted amount should be checked/sent as value while making inbound call to plug.
    • original amount should be used for claim accounting similar to earned fees.

Fast Switchboard

  • Implement the interface defined in #42
  • Should have a defined timeout after which roots are considered valid.
  • Keep a set of watchers
    • If all watchers confirm a root, it is considered valid before timeout is reached.
    • Watchers are allowed to trip fuses to stop packet execution even after timeout.
  • Maybe also include a global fuse to stop all execution.
  • Fee calculation can be done in #45

Original msg.sender context in payload

There is a good amount of demand from developers to know the original caller on destination Plug. However I do think for alot of applications this context wont be that useful.

Given that people can already do this by adding msg.sender to the payload on the srcChain we can just attempt to create a template that devs and inherit or add quickly to their Plug that adds the msg.sender to the payload before transmitting and then also extracts the msg.sender on the destinationPlug. Somewhat like this

Role management (seal/propose)

Make TransmitManager contract.

  • This contact is responsible to check signer and tell if it is allowed to make transmission for giver chain pair.
  • This check would be done for both seal and propose.
    function checkTransmitter(
        uint256 srcChainSlug,
        uint256 dstChainSlug,
        bytes32 root,
        bytes signature
    ) external returns (bool) {}
  • This contract is also responsible to check and collect transmission fees.
  • Fees will be paid while sending outbound message.
    function payFees(
        uint256 dstSlug
    ) external payable {}
  • We start with admin controlled TransmitManager. It has access control and ecrecover signature verification.
  • Seal fees calculated based on src gas price (runtime) and constant seal gas limit.
  • Propose fees calculated based on dst gas price (oracle) and constant propose gas limit.
  • Fees collected by admin and distributed manually.

Contracts: gas optimisations

Goal: Reduce the gas cost of functions based on usage frequency.

  • Outbound and execute functions are expected to be called most frequently and need to be optimized max.
  • Seal, propose and confirm will be called less frequently after batching is added. Need some optimization.
  • Other functions are related to setup and called very few times. We dont care about these.

Switchboard: Fee equation

Switchboards need to calculate the minimum fees needed to verify and execute a message.

  • Execute fees can be standardized as

    uint256 minExecutionFees = (executionOverhead + msgGasLimit) * dstGasPrice;
  • Verification fees depend on type of switchboard, following are some examples

    // optimistic
    uint256 minVerificationFees = 0;
    
    // 10 attestations
    uint256 minVerificationFees = 10 * attestGasLimit * dstGasPrice;
    
    // native arbitrum L1 to L2
    uint256 minVerificationFees = initiateGasLimit * srcGasPrice + arbitrumNativeFee;
    
    // native polygon L2 to L1
    uint256 minVerificationFees = initiateGasLimit * srcGasPrice + confirmGasLimit * dstGasPrice;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.