Optimism的挑战期

1. 引言

前序博客:

  • Optimism的Fault proof

用户将资产从OP主网转移到以太坊主网时需要等待一周的时间。这段时间称为挑战期,有助于保护 OP 主网上存储的资产。
而OP测试网的挑战期仅为60秒,以简化开发过程。

在这里插入图片描述

2. OP与L1数据交互

在这里插入图片描述

L1(以太坊)上的合约,可通过“bridging”,与L2(OP主网)上合约,进行交互。

同一网络内的Solidity合约调用,类似为:

contract MyContract {
    function doTheThing(address myContractAddress, uint256 myFunctionParam) public {
        MyOtherContract(myContractAddress).doSomething(myFunctionParam);
    }
}

MyContract.doTheThing会触发调用MyOtherContract.doSomething。在底层,Solidity通过向MyOtherContract合约发送ABI encoded call来触发调用doSomething函数。为简化开发者体验,此处抽象掉了很多这种复杂性。也可手工encoding(以更底层callabi.encodeCall函数)来实现相同的功能,如:

contract MyContract {
    function doTheThing(address myContractAddress, uint256 myFunctionParam) public {
        myContractAddress.call(
            abi.encodeCall(
                MyOtherContract.doSomething,
                (
                    myFunctionParam
                )
            )
        );
    }
}

以上两种调用方式等价。由于Solidity的限制,OP Stack的bridging接口被设计为看起来像第二个代码片段(即采用更底层的表达方式)。

2.1 L1与L2之间的基础通讯

从高层来看,L1与L2之间的数据发送流程,与以太坊上2个合约间的发送流程类似。L1与L2的通讯,可通过一组特殊的名为“messenger”的合约来实现。L1和L2有各自的messenger合约,用于抽象掉一些更底层的通讯细节,非常像HTTP库抽象掉物理网络连接。

每个messenger合约都有sendMessage函数,来向另一层的合约发送一个消息:

function sendMessage(
    address _target, //所调用的目标层上的合约
    bytes memory _message, //所发送的内容
    uint32 _minGasLimit //在目标层执行内容的最小gas limit
) public;

其等价为:

address(_target).call{gas: _gasLimit}(_message);

从而可调用个不同网络上的合约。
这掩盖了许多技术细节,这些细节使整件事在幕后运作,但这应该足以让你开始。想从以太坊上的合约调用OP主网上的合约吗?非常简单:

// Pretend this is on L2
contract MyOptimisticContract {
    function doSomething(uint256 myFunctionParam) public {
        // ... some sort of code goes here
    }
}
 
// And pretend this is on L1
contract MyContract {
    function doTheThing(address myOptimisticContractAddress, uint256 myFunctionParam) public {
        messenger.sendMessage(
            myOptimisticContractAddress,
            abi.encodeCall(
                MyOptimisticContract.doSomething,
                (
                    myFunctionParam
                )
            ),
            1000000 // or use whatever gas limit you want
        )
    }
}

具体可查看OP主网和测试网上合约地址 中的L1CrossDomainMessenger合约 和 L2CrossDomainMessenger合约。

2.2 通讯速度

不同于同一链上的合约调用,以太坊和OP主网间的调用不是即时的。跨链交易的通讯速度取决于方向:

  • 1)对于由L1->L2的交易:交易由L1(以太坊)到L2(OP主网),需约1到3分钟。因Sequencer需等待(包含该L1->L2交易区块之后的)一定数量的L1区块,以避免烦人的reorg问题。
  • 2)对于由L2->L1的交易:交易由L2(OP主网)到L1(以太坊),需要约7天。原因在于L1上的bridge合约,需等待该L2状态to be proven to the L1 chain之后,才能relay该message。
    由L2->L1的交易,分为4个不同的步骤:
    • 2.1)Step 1:将“给L1发送message”的L2交易,发送给Sequencer。这与其它L2交易类似,仅需数秒就可由Sequencer确认。
    • 2.2)Step 2:包含该L2交易的L2区块被提交给L1。这通常用时20分钟。
    • 2.3)Step 3:向L1上的OptimismPortal合约 提交该交易的proof。可在上面Step 2完成之后的任意时间提交。
    /// @notice Proves a withdrawal transaction.
    /// @param _tx              Withdrawal transaction to finalize.
    /// @param _l2OutputIndex   L2 output index to prove against.
    /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root.
    /// @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract.
    function proveWithdrawalTransaction(
        Types.WithdrawalTransaction memory _tx,
        uint256 _l2OutputIndex,
        Types.OutputRootProof calldata _outputRootProof,
        bytes[] calldata _withdrawalProof
    )
        external
        whenNotPaused
    {
        // Prevent users from creating a deposit transaction where this address is the message
        // sender on L2. Because this is checked here, we do not need to check again in
        // `finalizeWithdrawalTransaction`.
        require(_tx.target != address(this), "OptimismPortal: you cannot send messages to the portal contract");
    
        // Get the output root and load onto the stack to prevent multiple mloads. This will
        // revert if there is no output root for the given block number.
        bytes32 outputRoot = l2Oracle.getL2Output(_l2OutputIndex).outputRoot;
    
        // Verify that the output root can be generated with the elements in the proof.
        require(
            outputRoot == Hashing.hashOutputRootProof(_outputRootProof), "OptimismPortal: invalid output root proof"
        );
    
        // Load the ProvenWithdrawal into memory, using the withdrawal hash as a unique identifier.
        bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx);
        ProvenWithdrawal memory provenWithdrawal = provenWithdrawals[withdrawalHash];
    
        // We generally want to prevent users from proving the same withdrawal multiple times
        // because each successive proof will update the timestamp. A malicious user can take
        // advantage of this to prevent other users from finalizing their withdrawal. However,
        // since withdrawals are proven before an output root is finalized, we need to allow users
        // to re-prove their withdrawal only in the case that the output root for their specified
        // output index has been updated.
        require(
            provenWithdrawal.timestamp == 0
                || l2Oracle.getL2Output(provenWithdrawal.l2OutputIndex).outputRoot != provenWithdrawal.outputRoot,
            "OptimismPortal: withdrawal hash has already been proven"
        );
    
        // Compute the storage slot of the withdrawal hash in the L2ToL1MessagePasser contract.
        // Refer to the Solidity documentation for more information on how storage layouts are
        // computed for mappings.
        bytes32 storageKey = keccak256(
            abi.encode(
                withdrawalHash,
                uint256(0) // The withdrawals mapping is at the first slot in the layout.
            )
        );
    
        // Verify that the hash of this withdrawal was stored in the L2toL1MessagePasser contract
        // on L2. If this is true, under the assumption that the SecureMerkleTrie does not have
        // bugs, then we know that this withdrawal was actually triggered on L2 and can therefore
        // be relayed on L1.
        require(
            SecureMerkleTrie.verifyInclusionProof(
                abi.encode(storageKey), hex"01", _withdrawalProof, _outputRootProof.messagePasserStorageRoot
            ),
            "OptimismPortal: invalid withdrawal inclusion proof"
        );
    
        // Designate the withdrawalHash as proven by storing the `outputRoot`, `timestamp`, and
        // `l2BlockNumber` in the `provenWithdrawals` mapping. A `withdrawalHash` can only be
        // proven once unless it is submitted again with a different outputRoot.
        provenWithdrawals[withdrawalHash] = ProvenWithdrawal({
            outputRoot: outputRoot,
            timestamp: uint128(block.timestamp),
            l2OutputIndex: uint128(_l2OutputIndex)
        });
    
        // Emit a `WithdrawalProven` event.
        emit WithdrawalProven(withdrawalHash, _tx.sender, _tx.target);
    }
    
    • 2.4)Step 4:仅当fault挑战期(当前OP主网为7天) 结束之后,该交易才在L1上固化。该等待期是OP Stack安全模型的核心,无法规避。

2.3 访问msg.sender

合约会频繁使用msg.sender来基于calling address做决定。如,许多合约使用Ownable模式来选择性地约束对特定函数的访问。因为消息本质上是通过messenger合约在L1和L2之间穿梭的,所以当你接收到其中一条消息时,你会看到的 msg.sender将是与你所在的层相对应的messenger合约。
为此,每个messenger合约内均有xDomainMessageSender函数:

	/// @notice Retrieves the address of the contract or wallet that initiated the currently
    ///         executing message on the other chain. Will throw an error if there is no message
    ///         currently being executed. Allows the recipient of a call to see who triggered it.
    /// @return Address of the sender of the currently executing message on the other chain.
    function xDomainMessageSender() external view returns (address) {
        require(
            xDomainMsgSender != Constants.DEFAULT_L2_SENDER, "CrossDomainMessenger: xDomainMessageSender is not set"
        );

        return xDomainMsgSender;
    }

若你的合约被其中一个messenger合约调用,则可使用该函数来看实际上究竟是谁在发送该消息。在L2上实现onlyOwner modifier示例为:

modifier onlyOwner() {
    require(
        msg.sender == address(messenger)
        && messenger.xDomainMessageSender() == owner
    );
    _;
}

2.4 L1与L2之间发送数据的手续费

对于L1->L2交易:

  • L1->L2交易的大部分开销,源自L1上合约的执行。当发送L1->L2交易时,实际是调用了L1上的L1CrossDomainMessenger合约,然后调用L1上的OptimismPortal合约。这些在L1上的执行,会花费gas。L1->L2交易的总开销,最终由以太坊上的gas费决定。
  • L1->L2交易,会触发L2上的合约执行。OptimismPortal合约,会为L2执行,代为收取费用——burn掉动态量的L1 gas,具体量取决于所请求L2的gas limit。当更多人发送L1->L2交易时,所收取的L1 gas费会增加。当更少发送L1->L2交易时,所收取的L1 gas费会减少。
    • 由于代收取的L2执行gas量是动态的,所burn的gas在各个区块会各不相同。为避免gas燃尽,应总是为L1->L2交易gas limit增加至少20%的buffer。

对于L2->L1方向:

  • 由L2->L1的每个消息,需要3笔交易:
    • 1)L2交易:发起L2->L1启动交易,其定价与OP主网上的任何其他交易相同。
    • 2)L1证明交易:证明该L2交易。只有在L1上提交了包括该L2交易在内的L2区块后,才能提交此L1证明交易。该交易是昂贵的,因为它包括验证L1上的Merkle trie包含证明。
    • 3)L1固化交易:用于固化该L2交易。仅在该L2交易超过7天挑战期之后,才能提交L1固化交易。
  • L2->L1发送单个消息的总开销,包括:1笔L2启动交易和2笔L1交易开销。L1 proof交易和L1固化交易,通常要比L2启动交易贵得多。

2.5 挑战期

由L2->L1的消息,至少需要7天才能relay。这即意味着由L2发送的任意消息,只能过了一周的挑战期之后,才能在L1上收到。称其为“挑战期”的原因在于,在该期间,交易可被a fault proof挑战。
Optimistic Rollups是“乐观的”,因为其核心思想为:将某交易的结果发送到以太坊,而不在以太坊上实际执行该交易。在“乐观”情况下,该交易结果是正确的,可完全避免在以太坊上执行复杂(且昂贵)的逻辑。

但是,仍需要某种方式,来避免发布不正确的,而不是正确的,交易结果。为此,引入了“fault proof”。当某交易结果发布后,可将其看成“pending”一段时间,又名挑战期。在挑战期,任何人都可在以太坊上重新执行该交易,以试图证明所发布的结果是不正确的。

若某人证明该交易结果是错误的,则该结果将被删除,任何人都可以在该位置发布另一个结果(希望这次是正确的结果,经济惩罚会使错误的结果对发布者来说代价高昂)。一旦给定交易结果的窗口完全通过而没有受到质疑,则该结果可被视为完全有效(否则会有人对其提出质疑)。

无论如何,这里的重点是,在这个挑战期结束之前,不从L1上的智能合约内部对L1交易结果做出决定。否则,可能会根据无效的交易结果做出决策。结果,L2->L1 使用standard messenger合约发送的L1消息在等待整个挑战期之后才能被relay。

3. OP原理

为使L1gas开销最小化,OP Bedrock中的L2区块存储在以太坊链的非合约地址内:https://etherscan.io/address/0xff00000000000000000000000000000000000010。
这些L2区块作为交易calldata提交到以太坊,当该“交易”被包含在某具有足够attestations的区块内,则没法对其仅需修改或审查。从而OP主网可继承以太坊的可用性和完整性。

为降低开销,写入L1内的L2区块为压缩格式:
在这里插入图片描述
以压缩格式来在以太坊上存储L2区块,这很重要,因为写入到L1是OP主网交易的主要开销。

3.1 区块生成

Optimism区块生产主要由称为“Sequencer”的单一方管理,Sequencer主要提供如下服务:

  • 提供交易确认和状态更新。
  • 构建和执行 L2 区块。
  • 将用户交易提交到 L1。

在 Bedrock 中,Sequencer确实像以太坊那样有一个mempool,但该内存池是私有的,以避免为 MEV 提供机会。在 OP 主网中,区块每两秒生成一次,无论它们是空的(无交易)、被交易填充到区块 Gas limit、还是介于两者之间。

交易通过两种方式到达Sequencer:

  • 1)在 L1 上提交的交易(称为存款)包含在适当的 L2 链区块中。每个 L2 块都由“epoch”(它对应的 L1 块,通常发生在 L2 区块之前几分钟)及其在该epoch内的序列号来标识。该epoch的第一个区块包含其对应的 L1 区块中发生的所有存款。如果epoch试图忽略合法的 L1 交易,它最终会得到与Verifier不一致的状态,就像epoch试图通过其他方式伪造状态一样。这为 OP 主网提供了 L1 以太坊级别的抗审查能力。 可在协议规范Deriving the Transaction List中阅读有关此机制的更多信息。
  • 2)交易直接提交给Sequencer。这些交易的提交成本要低得多(因为不需要单独的 L1 交易费用),但它们当然不能抵抗审查,因为Sequencer是唯一了解它们的参与者。

目前,Optimism基金会在 OP 主网上运行唯一的区块生产者。有关未来计划对 Sequencer 角色进行去中心化。

在这里插入图片描述
基本工作流程为:

  • 1)用户将交易发送到主处理程序(Sequencer),然后主处理程序在其版本的第 2 层 (L2) 链上处理这些交易。
  • 2)处理后,Sequencer将交易详细信息和更新的第 2 层状态发送到第 1 层 (L1)。
  • 3)然后,其他第 2 层节点使用此交易更新其第 2 层链的版本。
  • 4)为了检查准确性,Verifier验证者节点将其更新状态与排序器提交的状态进行比较。

3.2 区块执行

op-geth组件所实现的执行引擎,使用两种机制接收区块:

  • 1)执行引擎可以使用对等网络与其他执行引擎进行自我更新。这与 L1 执行客户端通过网络同步状态的方式相同。具体见happy-path sync。
  • 2)op_node组件所实现的rollup节点从 L1 派生出 L2 区块。这种机制速度较慢,但​​具有抗审查性。具体见Worst-case sync。

3.3 在各层之间bridge ETH或token

Optimism 的设计目的是让用户可以在 L2(OP 主网、OP Sepolia 等)和底层 L1(以太坊主网、Sepolia 等)上的智能合约之间发送任意消息。这使得在两个网络之间传输 ETH 或代币(包括 ERC20 代币)成为可能。这种通信发生的确切机制根据消息发送的方向而有所不同。

OP 主网在标准桥中使用此功能,允许用户将代币从以太坊存入 OP 主网,也允许将代币从 OP 主网提取回以太坊。详情见Using the Standard Bridge。

  • 1)从以太坊迁移到 OP 主网:从以太坊(L1)到 OP 主网(L2)的交易称为存款。使用L1CrossDomainMessenger合约 或 L1StandardBridge合约,存款交易成为与存款所在的 L1 区块相对应的“epoch”的第一个 L2 区块中规范区块链的一部分。该 L2 块通常会在相应的 L1 块几分钟后创建。详情见Deposits。
  • 2)从 OP 主网迁移到以太坊:提款分为3个阶段:【详情见Withdrawals】
    • 2.1)通过 L2 交易初始化提款。
    • 2.2)等待下一个输出根提交到L1,然后使用 提交提款证明proveWithdrawalTransaction。这一新步骤可以对提款进行链下监控,从而更容易识别不正确的提款或输出根。这可保护 OP 主网用户免受一系列潜在的桥接漏洞的影响。
    • 2.3)故障挑战期结束后(主网一周,比测试网短),完成提现。

3.4 Fault proof 故障证明

在 Optimistic Rollup 中,状态承诺被发布到 L1(OP 主网的以太坊),没有任何直接证据证明这些承诺的有效性。相反,这些承诺被视为在一段时间内悬而未决(称为“挑战窗口”)。如果拟议的状态承诺在挑战窗口(当前设置为 7 天)期间没有受到挑战,则该承诺将被视为最终承诺。一旦承诺被认为是最终的,以太坊上的智能合约就可以安全地接受基于该承诺的有关 OP 主网状态的提款证明。

当状态承诺受到质疑时,可以通过“fault proof”过程使其无效。如果该承诺被成功质疑,那么它将被从承诺中删除,StateCommitmentChain(SCC)最终被另一个提议的承诺所取代。值得注意的是,成功的挑战不会回滚 OP 主网本身,只会回滚有关链状态的已发布承诺。交易的顺序和 OP 主网的状态不会因故障证明挑战而改变。

在这里插入图片描述

  • SCC:状态承诺链(SCC)合约包含提议的状态根列表,提议者断言这些状态根是规范交易链(CTC)中每笔交易的结果。这里的元素与CTC中的交易一一对应。
  • CTC:规范交易链(CTC)是必须应用于 OVM 状态的交易协议。
  • OVM:乐观虚拟机是一个符合第 2 层 (L2) 要求的执行框架,使汇总实现能够与以太坊的主区块链进行通信。
  • GETH:Geth 是以太坊区块链的执行客户端,用于处理交易。智能合约的部署和执行包括内置的 EVM。

作为 2021 年 11 月 11 日EVM 等效性的副作用,fault proof流程目前正在进行重大重新开发更新。

4. 为什么 Optimistic Rollup 挑战期为 7 天?

提款延迟是 Optimistic Rollup 的基本组成部分。当用户向以太坊提出有关 Optimistic Rollup 状态的声明时,就会开始提款。 如,这个声明可能是“我在 Optimism 上烧掉了 20 个代币,所以让我在以太坊上提取 20 个代币。”
由于 Optimistic Rollup 的重点在于 L1 并未实际执行 L2 链,因此 L1 不知道此声明是否有效。ZK Rollups 通过为 L1 提供一个加密证明来证明给定的声明是有效的,从而解决了这个问题。乐观汇总通过要求声明必须通过挑战期才能被视为有效来解决此问题。每个claim必须等待一段挑战期,在此期间挑战者可以声明该claim无效。如果有人对某个主张提出质疑,那么就会开始一些链上游戏来确定该主张是否真正有效。

由于某人检测无效声明并提交挑战可能需要一些时间,因此我们不可避免地需要挑战期大于零。毕竟,如果挑战期的持续时间是零秒,那么就没有机会提交挑战了。那么我们的问题就变成了:挑战期应该是多长?

最终状态的现代乐观汇总挑战游戏本质上采取提出主张的用户和质疑该主张的用户之间来回的形式(实际上,这些协议通常被设计为允许任何人参与要么是“团队”,但现在让我们保持简单)。为了举例,我们假设整个过程中有大约 10 个来回步骤(确切的数量有所不同,但在这里并不重要)。

如果双方都非常非常快,那么整个挑战过程至少需要 10 个以太坊区块(2 分钟)。当然,用户并不是像这样完全快,因此您可能需要添加一些至少是基线数字 10 倍的填充,即大约 100 个块(20 分钟)。尽管如此,100 个区块还是比进入 7 天挑战期的 50400 多个区块要短得多。这里一定还有别的东西。

攻击者可以用他们的钱做什么?攻击者的目标是阻止挑战者将他们的挑战交易包含在链上。毕竟,如果挑战交易成功,那么攻击就会失败。攻击者基本上有三种潜在的策略:

  • 1)对挑战者进行直接 DoS 攻击,首先阻止他们与 L1 网络交互
  • 2)通过昂贵的交易向 L1 网络发送垃圾邮件,以推高 Gas 价格并阻止挑战者进行交易
  • 3)通过控制大量验证者来直接审查挑战者

为什么挑战期是7天?

  • 因为它比保守的餐巾纸数学下限要长得多,也许更重要的是,它为整个以太坊社区留下了足够的时间陷入大规模困境。简而言之,利用 Optimistic Rollup 所需的攻击类型会在很长一段时间内显着降低以太坊上的交易体验。每个人都会非常生气。诚实的验证者会突然出现,愿意提交挑战交易以阻止攻击。一周给了我们足够的时间来协调社交层面的这种恢复。

更多详细内容,见:

  • Why is the Optimistic Rollup challenge period 7 days?

5. OP发展历程

OP发展历程为:

  • 2019年
    • 6 月,Plasma Group 开发人员创建了一个名为 Optimism Rollup 的可扩展性解决方案
  • 2020年
    • 2月,兼容EVM的乐观虚拟机(OVM)上线测试阶段
    • 9月,推出带有Optimism Rollup解决方案的测试网
  • 2021年
    • 一月份,Optimism 推出了 Alpha 版本
    • 10月,EVM主网升级启动
    • 8 月,以太坊和 Optimism 之间的成熟区块链桥梁启动。该桥此前只允许传输已启用的 ERC-20 代币
    • 12 月,加密世界庆祝 Optimism 主网发布
  • 2022年
    • 4月,宣布成立乐观集体(治理实验),并进行了多次空投。由于这一治理变化,乐观主义人民银行被解散,并创建了一个非营利组织——乐观主义基金会
    • 5 月,$OP 币开始分配

参考资料

[1] 2023年4月 为什么从 OP 主网转出资产需要等待一周?
[2] Sending Data Between L1 and L2
[3] L2IV RESEARCH 2024年1月29日 Why we invested in Rome Protocol? The Solana-Based Shared Sequencer
[4] Dispute Game
[5] Rollup Protocol Overview
[6] Withdrawal Flow
[7] Why is the Optimistic Rollup challenge period 7 days?
[8] 2023年10月博客 Ethereum’s Optimistic Future: An Introduction to Optimistic Rollups
[9] Optimism

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/359583.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32学习笔记二——STM32时钟源时钟树

目录 STM32芯片内部系统架构详细讲解: 1.芯片内部混乱电信号解决方案: 2.时钟树: 1.内部RC振荡器与外部晶振的选择 2. STM32 时钟源 3.STM32中几个与时钟相关的概念 4.时钟输出的使能及其流程 5.时钟设置的基本流程 时钟源——单片机…

上海亚商投顾:创业板指失守1600点 全市场超5000只个股下跌

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 沪指昨日低开低走,深成指跌超2%,创业板指失守1600点,续创年内新低。脑机接…

C语言KR圣经笔记 6.6 表查询 6.7 typedef

6.6 表查询 为了说明结构体的更多方面,本节我们来写一个表查询功能包的内部代码。在宏处理器或编译器的符号表管理例程中,这个代码是很典型的。例如,考虑 #define 语句,当遇到如下行 #define IN 1 时,名称 IN 与其对…

n-皇后-dfs

import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Scanner;public class Main {static int n,N 20; //这里只会用到2 * n - 1的格子,开大点保险static char[][] g new c…

Makefile编译原理 makefile中的include关键字

一.makefile中的include关键字 类似C语言中的include 将其他文件的内容原封不动的搬入当前文件 make对include关键字的处理方式: 在当前目录搜索或指定目录搜索目标文件 搜索成功:将文件内容搬入当前makefile中 搜索失败:产生警告&…

聚观早报 | 360 AI搜索App上线;岚图汽车与京东达成合作

聚观早报每日整理最值得关注的行业重点事件,帮助大家及时了解最新行业动态,每日读报,就读聚观365资讯简报。 整理丨Cutie 1月30日消息 360 AI搜索App上线 岚图汽车与京东达成合作 三星电子在硅谷新设实验室 小米平板7系列参数曝光 Spa…

大创项目推荐 题目:基于深度学习的中文对话问答机器人

文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分:4.2 损失函数:4.3 搭建seq2seq框架:4.4 测试部分:4.5 评价NLP测试效果:4.6 梯度截断…

代码随想录算法刷题训练营day20

代码随想录算法刷题训练营day20:LeetCode(654)最大二叉树、LeetCode(617)合并二叉树、LeetCode(700)二叉搜索树中的搜索、LeetCode(700)二叉搜索树中的搜索、LeetCode(98)验证二叉搜索 LeetCode(654)最大二叉树 题目 代码 import java.util.Arrays;/*** Definit…

MATLAB有限元应用-四边形八节点梁受力弯曲

MATLAB在处理平面有限元问题和梁弯曲问题上有很强的能力,主要体现在以下几个方面: 建模与网格划分 MATLAB内置了方便的图形界面工具(pdetoolbox等),可以快速对几何模型进行二维三维网格划分,生成有限元分析需要的网格。 求解器 MATLAB内置了多种求解偏微分方程的有限元求解器…

大模型重塑车载语音交互:赛道巨头如何引领新周期?

车载语音交互赛道正进入新一轮竞争周期。 高工智能汽车注意到,传统车载语音交互赛道当前基本已进入成熟期,主要为任务型助手,包括从单轮对话到多轮对话,单音区到多音区,从单一的导航、多媒体娱乐等座舱功能扩展智能驾…

钢材表面缺陷YOLOV8,OPENCV调用

【免费】钢材表面缺陷YOLOV8资源-CSDN文库 钢材表面缺陷YOLOV8NANO,训练得到PT模型,然后转换成ONNX,OPENCV的DNN调用,支持C,PYTHON,ANDROID

VScode中使用Xdebug调试PHP

君衍. 一、下载VScode与PHPstudy二、配置PHP环境变量三、PHPstudy中启用xdebug扩展四、打开php.ini,修改配置五、修改vscode配置六、VScode安装相关插件七、配置launch.json八、设置断点,开始调试 一、下载VScode与PHPstudy 首先我们自然是需要搭建环境…

C++ 数论相关题目 博弈论 Nim游戏

给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。 问如果两人都采用最优策略,先手是否必胜。 输入格式…

《统计学习方法:李航》笔记 从原理到实现(基于python)-- 第5章 决策树(代码python实践)

文章目录 第5章 决策树—python 实践书上题目5.1利用ID3算法生成决策树,例5.3scikit-learn实例 《统计学习方法:李航》笔记 从原理到实现(基于python)-- 第5章 决策树 第5章 决策树—python 实践 import numpy as np import pand…

Docusaurus 文档侧边栏增加 New 标识

在使用 Docusaurus 搭建文档站点的时候,我们经常要给某个侧边栏菜单增加一些醒目的标识,比如针对新创建的文档给它一个 New 的标识, 以提醒过来看文档的用户这是一个新增加项或者新特性(阅读的时候不要遗漏)。 然而这个…

C#: form 添加窗体最小化事件,添加系统托盘图标,点击后可以打开、最小软件窗口

说明: 1.实现窗体在最小化后触发一个事件,可以去实现需要的功能。 2.最小化后软件图标出现在系统右下角的托盘串口。 3.点击托盘口的图标可以实现软件弹出窗口和最小化的切换。 1.参考办法 以下是判断C#窗体最小化到状态栏的状态的方法:…

[AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗

前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言4.5key价格泄漏ChatGPT4.0使用地址ChatGPT正确打开方式最新功能语音助手存档…

Redis核心技术与实战【学习笔记】 - 7.Redis GEO类型 - 面向 LBS 应用的数据类型

前言 前面,介绍了 Redis 的 5 大基本数据类型:String、List、Hash、Set、Sorted Set,它们可以满足绝大多数的数据存储需求,但是在面对海里数据统计时,它们的内存开销很大。所以对于一些特殊的场景,它们是无…

计算机网络-物理层设备(中继器 集线器)

文章目录 中继器中继器的功能再生数字信号和再生模拟信号同一个协议 集线器(多口中继器)不具备定向传输的原因集线器是共享式设备的原因集线器的所有接口都处于同一个碰撞域(冲突域)内的原因 小结 中继器 中继器的功能 中继器的…

JVM 内存模型

1 什么是 JVM 内存模型 JVM 需要使用计算机的内存,Java 程序运行中所处理的对象或者算法都会使用 JVM 的内 存空间,JVM 将内存区划分为 5 块,这样的结构称之为 JVM 内存模型。 2 JVM 为什么进行内存区域划分 随着对象数量的增加&#xff…
最新文章