智能合约调试指南

2023-11-08

不像你在其他地方看到的纸质合约,以太坊的智能合约是代码组成的,需要你以非常谨慎的态度去对待它。

(这是一件好事,想象下如果现实世界的合同需要编译的话会更清晰么?)

如果我们的合同没有被正确的编码出来, 我们的交易可能会失败,导致以太币的损失(以 gas 的形式),更不用说浪费时间和精力。

幸运的是,Truffle (版本 4 以上) 内置了逐步调试的功能,所以一旦发生错误,你可以很快发现并修复它。

在本教程中,我们将在测试的区块链环境中部署一个基础的合同,并引入一些错误,通过 Truffle 内置调试器修复它们。

一个基础的智能合约

一个最基础的合同是一个简单的存储类型的智能合约。(这个例子改编自 Solidity documentation)

pragma solidity ^0.4.17;

contract SimpleStorage {
  uint myVariable;

  function set(uint x) public {
    myVariable = x;
  }

  function get() constant public returns (uint) {
    return myVariable;
  }
}

此合约做了两件事:

  • 允许你设置一个变量(myVariable)为特定整数值。
  • 允许你查询一个选定的值。

这不是一个非常有趣的合约,但是这不是重点。我们想看看出错后会发生什么当事情。

首先我们配置环境。

部署智能合约

  1. 首先为我们的合约创建一个新的本地目录:
mkdir simple-storage
cd simple-storage

2. 创建一个空的 Truffle 项目

truffle init

这个命令将创建目录,比如contracts/和migrations/,并生成一些文件用于帮助部署合约到区块链。

3.contracts/目录中有一个Store.sol文件。

pragma solidity ^0.4.17;
contract SimpleStorage {
  uint myVariable;
function set(uint x) public {
    myVariable = x;
  }
function get() constant public returns (uint) {
    return myVariable;
  }
}

这是我们需要调试的合约,详细的合约内容的解释超出了本教程的范围,注意我们有一个名为SimpleStorage的合约,里面有个数字类型的变量myVariable和两个函数set()和get()。第一个函数设置变量内容,第二个获取变量。

4. migrations/ 目录下,有个 2_deploy_contracts.js 文件。

var SimpleStorage = artifacts.require("SimpleStorage");
module.exports = function(deployer) {
  deployer.deploy(SimpleStorage);
};

这个文件是管理部署SimpleStorage合约的。

5. 在终端中,编译此合约

truffle compile

6. 再开一个终端,运行 truffle develop ,开启 truffle 内置的测试区块链,这样我们可以使用它来测试合约。

truffle develop

这个命令将出现提示符 truffle(develop)>, 从现在开始,所有的命令都在此提示符下输入(特殊情况会说明)

7. 当开发控制台运行起来后,我们可以部署我们的合约了。

migrate

下面的相应有些类似,除了 id 不同。

Running migration: 1_initial_migration.js
   Replacing Migrations...
   ... 0xe4f911d95904c808a81f28de1e70a377968608348b627a66efa60077a900fb4c
   Migrations: 0x3ed10fd31b3fbb2c262e6ab074dd3c684b8aa06b
 Saving successful migration to network...
   ... 0x429a40ee574664a48753a33ea0c103fc78c5ca7750961d567d518ff7a31eefda
 Saving artifacts...
 Running migration: 2_deploy_contracts.js
   Replacing SimpleStorage...
   ... 0x6783341ba67d5c0415daa647513771f14cb8a3103cc5c15dab61e86a7ab0cfd2
   SimpleStorage: 0x377bbcae5327695b32a1784e0e13bedc8e078c9c
 Saving successful migration to network...
   ... 0x6e25158c01a403d33079db641cb4d46b6245fd2e9196093d9e5984e45d64a866
 Saving artifacts...

和基础的智能合约交互

智能合约通过truffle develop部署到了测试网络中,运行 console 而不是 Ganache,一个内置在 Truffle 的本地区块链。

  1. 在truffle develop运行的终端中,输入如下命令:
SimpleStorage.deployed().then(function(instance){return instance.get.call();}).then(function(value){return value.toNumber()});

这个命令找到 SimpleStorage 合约,然后调用get()命令,通常返回一个字符串并转化为数字。

myVariable被设置为 0,尽管我们还没给它赋值。这是因为 Solidity 数值型变量会自动被赋值为 0,不像其他语言会是NULL和undefined。

2. 现在我们运行一条交易命令,调用set()设置我们的变量为其他值。

SimpleStorage.deployed().then(function(instance){return instance.set(4);});

设置变量为 4,返回的信息包括交易(交易 id ,交易receipt 和一些交易的时间 log)

{ tx: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42',
   receipt:
{ transactionHash: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42',
  transactionIndex: 0,
  blockHash: '0x60adbf0523622dc1be52c627f37644ce0a343c8e7c8955b34c5a592da7d7c651',
  blockNumber: 5,
  gasUsed: 41577,
  cumulativeGasUsed: 41577,
  contractAddress: null,
  logs: [] },
   logs: [] }

最重要的是交易的 id (在这里是 tx 和 transactionHash)。我们需要赋值这个值用来调试。

3. 想验证值是否已经改变,运行get()

SimpleStorage.deployed().then(function(instance){return instance.get.call();}).then(function(value){return value.toNumber()});

输出

4

调试错误

上文展示了智能合约如何工作,现在我们引入一些错误到合约中。

针对如下几个错误

  • 无限循环
  • 无效错误检查
  • 无错误,但是函数没有按预期执行

错误 #1: 无限循环

在以太坊区块链中,交易不能永远执行下去。

一个交易会一直执行到 gas 用尽。一旦发生这种情况,交易会返回out of gas错误。

因为 gas 是以以太币计费的,所以会造成真实的资产损失,所以修复这种错误迫在眉睫。

引入错误

  1. 在编辑器打开contracts/目录中的Store.sol
  2. 替换set()
function set(uint x) public {
  while(true) {
    myVariable = x;
  }
}

因为while(true)所以函数永远不会退出。

测试合约

Truffle 的开发终端不重启就可以重新部署合约。我们可以通过migrate一步编译和部署合约

  1. Truffle 的开发终端中,更新合约
migrate --reset

2. 为了更好的捕获错误,我们打开第二个终端

truffle develop --log

然后回到之前的终端中

3. 现在我们可以运行交易了,运行set()命令

SimpleStorage.deployed().then(function(instance){return instance.set(4);});

捕获到错误

Error: VM Exception while processing transaction: out of gas

在 log 的终端中,我们可以看到更多信息

develop:testrpc eth_sendTransaction +0ms
 develop:testrpc  +1s
 develop:testrpc   Transaction: 0xe493340792ab92b95ac40e43dca6bc88fba7fd67191989d59ca30f79320e883f +2ms
 develop:testrpc   Gas usage: 4712388 +11ms
 develop:testrpc   Block Number: 6 +15ms
 develop:testrpc   Runtime Error: out of gas +0ms
 develop:testrpc  +16ms

通过这些信息,我们可以调试这个交易

调试错误

调出 debug 的命令是在 Truffle 开发终端输入debug <Transaction ID>或者直接在终端中输入truffle debug <Transaction ID>,现在让我们开始吧!

  1. 在 Truffle 开发终端中,复制粘贴交易 id 到 debug 命令后
debug 0xe493340792ab92b95ac40e43dca6bc88fba7fd67191989d59ca30f79320e883f

你将看到如下输出

Gathering transaction data...
Addresses affected:
     0x377bbcae5327695b32a1784e0e13bedc8e078c9c - SimpleStorage
Commands:
   (enter) last command entered (step next)
   (o) step over, (i) step into, (u) step out, (n) step next
   (;) step instruction, (p) print instruction, (h) print this help, (q) quit
Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:
1: pragma solidity ^0.4.17;
   2:
   3: contract SimpleStorage {
      ^^^^^^^^^^^^^^^^^^^^^^^
debug(develop:0xe4933407...)>

这是个交互式命令行,你可以用列出的命令和程序交互

1. 最常用的命令是 `step next`,命令执行一次往下一行代码,快捷键是 `Enter` 或者 `n`
输出

 Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:
    4:   uint myVariable;
    5:
    6: function set(uint x) public {
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

注意程序已经移动到了下一个命令,第六行中。
2. 键入回车

 Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

        5:
        6: function set(uint x) public {
        7:   while(true) {
        ^^^^^^^^^^^^

3. 不断按回车

 Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

        5:
        6: function set(uint x) public {
        7:   while(true) {
               ^^^^

        debug(develop:0xe4933407...)>

        Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

        5:
        6: function set(uint x) public {
        7:   while(true) {
         ^^^^^^^^^^^^

        debug(develop:0xe4933407...)>

        Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

        6: function set(uint x) public {
        7:   while(true) {
        8:     myVariable = x;
                        ^

        debug(develop:0xe4933407...)>

        Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

        6: function set(uint x) public {
        7:   while(true) {
        8:     myVariable = x;
           ^^^^^^^^^^

        debug(develop:0xe4933407...)>

        Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

        6: function set(uint x) public {
        7:   while(true) {
        8:     myVariable = x;
           ^^^^^^^^^^^^^^

        debug(develop:0xe4933407...)>

        Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

        5:
        6: function set(uint x) public {
        7:   while(true) {
         ^^^^^^^^^^^^

请注意,最终步骤会一直重复。事实上,按回车会永远重复那些交易(直到用完 gas )。而这会告诉你问题在哪里。

错误 #2: 无效错误检查

智能合约可以用 assert() 来保证必要特定条件会出现。这种和合约状态的冲突是不可调和的。
引入错误
现在我们来引入这个错误,看看调试器如何发现它。
1. 再次打开 `Store.sol`
2. 替换 `set()` 函数

 function set(uint x) public {
      assert(x == 0);
      myVariable = x;
    }

和前一个版本一样,只是多了 `assert()` 函数,保证 `x == 0`,如果我们设置 x 为其他值,我们就会发现错误。

测试合约
和之前一样,我们重置下合约
1. migrate --reset
2. SimpleStorage.deployed().then(function(instance){return instance.set(4);});
我们会看到如下错误

 Error: VM Exception while processing transaction: invalid opcode

调试错误
1. 复制交易 id 到 debug 命令下

debug 0xe493340792ab92b95ac40e43dca6bc88fba7fd67191989d59ca30f79320e883f

回到调试器

Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:
1: pragma solidity ^0.4.17;
   2:
   3: contract SimpleStorage {
      ^^^^^^^^^^^^^^^^^^^^^^^
debug(develop:0xe4933407...)>

1. 键入回车

Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:

    5:
    6:   function set(uint x) public {
    7:     assert(x == 0);
           ^^^^^^^^^^^^^^

    debug(develop:0x7e060037...)>

    Transaction halted with a RUNTIME ERROR.

    This is likely due to an intentional halting expression, like 
    assert(), require() or revert(). It can also be due to out-of-gas
    exceptions. Please inspect your transaction parameters and 
    contract code to determine the meaning of this error.

我们可以看到最后的事件中触发了错误.

错误 #3: 无错误,但是函数没有按预期执行

有时候,错误不一定是真正的错误,它在运行时间内不会引起问题,只是不会按预期执行。
举个例子,一个事件将会在变量是奇数的时候执行,而另一个事件在偶数的时候执行。如果我们调换了这个条件,让相反的事件执行了。它斌不会触发错误,然而,合约会不按照我们的预期执行下去。
我们再次用调试器来找出错误。
引入错误
1. 再次打开 `Store.sol`
2. 替换 `set()` 函数

 event Odd();

    event Even();

    function set(uint x) public {
      myVariable = x;
      if (x % 2 == 0) {
        Odd();
      } else {
        Even();
      }
    }

代码有两个假的事件,`Odd()` 和 `Even()` 是否执行取决于 x 是否能被 2 整除。
但是我们发现 x 能被 2 整除时, `Odd()` 事件触发了。
测试合约
1. migrate --reset
2. SimpleStorage.deployed().then(function(instance){return instance.set(4);});
没有错误产生,输出如下

{ tx: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42',
  receipt:
   { transactionHash: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42',
     transactionIndex: 0,
     blockHash: '0x08d7c35904e4a93298ed5be862227fcf18383fec374759202cf9e513b390956f',
     blockNumber: 5,
     gasUsed: 42404,
     cumulativeGasUsed: 42404,
     contractAddress: null,
     logs: [ [Object] ] },
  logs:
   [ { logIndex: 0,
       transactionIndex: 0,
       transactionHash: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42',
       blockHash: '0x08d7c35904e4a93298ed5be862227fcf18383fec374759202cf9e513b390956f',
       blockNumber: 5,
       address: '0x377bbcae5327695b32a1784e0e13bedc8e078c9c',
       type: 'mined',
       event: 'Odd',
       args: {} } ] }

logs 里面显示调用了 Odd 事件,这是不对的,我们的任务是找到这个事件为什么会被触发。
调试错误
1. 复制交易 id 到 debug 命令下

debug 0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42

2. 键入回车几次,最后我们将看到调用 Odd 事件的条件

Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:
10:   function set(uint x) public {
11:     myVariable = x;
12:     if (x % 2 == 0) {
        ^^^^^^^^^^^^^^^^
debug(develop:0x7f799ad5...)>
Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c:
11:     myVariable = x;
12:     if (x % 2 == 0) {
13:       Odd();
          ^^^^^
debug(develop:0x7f799ad5...)>

错误找到了,这个条件导致了错误的事件调用。

结论

有了在 Truffle 中的调试能力,你可以编写更健壮的智能合约。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

智能合约调试指南 的相关文章

  • 智能合约(一)————智能合约入门

    1 智能合约的基本组成 1 1 程序版本 1 2 合约声明 1 3 状态变量 1 4 合约方法 在这里constant相当于他声明这个局部变量不能更改 但是他并没有实际作用 实际就只是警示作用 2 地址adress address bala
  • 深入理解Solidity——独立汇编

    独立汇编 Standalone Assembly 上面介绍的在Solidity中嵌入的内联汇编语言也可以单独使用 实际上 它是被计划用来作为编译器的一种中间语言 在这个目的下 它尝试达到下述的目标 使用它编写的代码要可读 即使代码是从Sol
  • 一、Conflux 网页钱包创建

    相信每一位新加入的小伙伴都有一个疑问 去中心化的钱包到底是什么 如何拥有一个自己钱包 如何使用这个钱包吧 下面我将会为大家讲解如何创建钱包 导入钱包 使用钱包三个方面讲解下 一 创建钱包 首先大家进入钱包网站登录页面 大家可以将该网址收藏
  • conflux.web3j.RpcException:data = “\“network prefix inconsistent in from(net999) and to(cfxtest)\““

    java conflux sdk报错 SLF4J Failed to load class org slf4j impl StaticLoggerBinder SLF4J Defaulting to no operation NOP log
  • 区块链职业培训任重道远,四个方向可入行

    作者 赛联区块链教育 张群 区块链已经来到世界14年了 中国成为重大战略也三年了 这两年区块链产业发展十分迅猛 以北京 杭州 上海 深圳 重庆 成都为代表的的区块链产业迅速崛起 成为中国区块链发展的领军者 由于在教育圈的原因 最近几年和区块
  • Solidity transfer,call和send 的区别

    address transfer throws on failure forwards 2 300 gas stipend not adjustable safe against reentrancy should be used in m
  • 以太坊Dapp终极教程——如何构建一个完整的全栈去中心化应用(三)

    在以太坊Dapp终极教程 如何构建一个完整的全栈去中心化应用 一 中 我们已经完成了一切所需的设置 在以太坊Dapp终极教程 如何构建一个完整的全栈去中心化应用 二 中 让我们通过列出将在选举中运行的候选人来继续构建智能合约并完成客户端程序
  • fisco-bcos区块链webase浏览器无法加载验证码,报错无法登陆

    fisco bcos区块链webase浏览器无法加载验证码 报错无法登陆 浏览器里验证码加载不出来 图中显示的是webase nodemanager模块出错 检查log文件 发现nodemanager模块下确实报错 但再细查后发现实际上问题
  • 区块链之java(一) 番外篇(数据类型)

    预先善其事 必先利其器 今天俺们讲讲智能合约和java中的一个数据类型 在智能合约中 大概有这些基本类型 能满足开发 是否有漏的呢 我也不太清楚 因为我也没有很深入的了解这块 好了 话不多说 看看具体的东西 智能合约类型如下 uint256
  • 莱昂哈德·欧拉生平及其成就简介

    莱昂哈德 欧拉 Leonhard Euler 1707年4月15日 1783年9月18日 瑞士数学家 自然科学家 1707年4月15日出生于瑞士的巴塞尔 1783年9月18日于俄国圣彼得堡去世 欧拉出生于牧师家庭 自幼受父亲的影响 13岁时
  • 在区块链上开发可更新的智能合约

    由于区块链不可篡改的特性 智能合约一旦部署在区块链上 其执行的逻辑就无法再更改 长期来看 这个重要的特性反而限制了智能合约的弹性和发展 接下来要介绍如何设计及部署合约才能让合约在需要时可以更新 但这里的更新意思不是修改已经部署的合约 而是部
  • 使用 sCrypt 实现一个简单的 NFT 合约

    我们之前的token方案针对的是可替换 fungible 的 token 这里来看看另一种方案如何实现 NFT non fungible token 合约 这类 token 可以代表独一无二的和不可分割的资产 比如房地产和收藏品 概览 与可
  • 智能合约漏洞案例,DEI 漏洞复现

    智能合约漏洞案例 DEI 漏洞复现 1 漏洞简介 https twitter com eugenioclrc status 1654576296507088906 2 相关地址或交易 https explorer phalcon xyz t
  • 以太坊Python智能合约开发指南

    在以太坊上获得一个基本的智能合约是一个很简单的事 只需google查询 ERC20代币教程 你会发现有关如何做到这一点的大量信息 以编程方式与合约交互完全是另一回事 如果你是一个Python程序员 那么教程就很少 所以写这个Python中的
  • 基于BSC测试网收益聚合器Beefy协议的编译、测试、部署

    前言 文章主要介绍了收益聚合器Beefy协议在币安智能链测试网网上的编译测试部署流程 以Pancake上的USDC BUSD最新Curve版流动池的农场质押为例 详细介绍了完整的操作流程 准备工作 Node js环境 https nodej
  • Node.js web3.js编译、部署智能合约

    Node js web3 js编译 部署智能合约 供参考脚本 https github com Saturday24 Smart Contracts Script 1 编译脚本 a install web3 solc fs path b 编
  • 区块链之java调用智能合约(二)部署智能合约

    前言 上一节 已经说过 如何的创建一个合约 如何编译合约 然后在java中调用 但是呢 这些还远远不够 那么还差哪些呢 现在就是如何将创建的智能合约部署的对应的共链 私链 测试链中了 需要部署后 才能真正的使用 现在就讲讲如何部署智能合约
  • 网络化,元宇宙的门槛

    如果森林中的一棵树倒下 但周围没有人听到 那它是否会发出声音 这一思想实验可以追溯到数百年前 这个实验之所以经久不衰 部分原因是它很有趣 而它之所以很有趣 是因为它耐人寻味并且融人了哲思 人们通常认为 上面这个问题最初是由主观唯心主义哲学家
  • 浅谈以太坊智能合约的设计模式与升级方法

    浅谈以太坊智能合约的设计模式与升级方法 1 最佳实践 2 实用设计案例 2 1 控制器合约与数据合约 1 gt 1 2 2 控制器合约与数据合约 1 gt N 2 3 控制器合约与数据合约 N gt 1 2 4 控制器合约与数据合约 N g
  • 区块链应用开发(智能合约的开发和WeBASE合约IDE的使用)

    文章目录 四 智能合约的开发和WeBASE合约IDE的使用 一 实验概述 二 实验目标 三 实验环境及建议 四 实验步骤 4 1 启动Webase 4 2 智能合约开发 4 2 1 合约功能设计 4 2 2 存证合约开发 4 2 3 工厂合

随机推荐

  • 深度学习原理分析之数据不足与过拟合

    人们常常知道若干种解决过拟合的方法但不知其因 本文对其进行原理剖析 一个模型所能提供的信息一般来源于两个方面 一是训练数据中蕴含的信息 二是在模型的形成过程中 包括构造 学习 推理等 人们提供的先验信息 当训练数据不足时 说明模型从原始数据
  • Spring Boot官方例子《Developing Your First Spring Boot Application》无法运行

    官方的第一个例子就卡住了 https docs spring io spring boot docs current reference htmlsingle getting started first application 按照要求 一
  • 【消息队列】kafka consumer demo

    package consumer import org apache kafka clients consumer ConsumerConfig import org apache kafka clients consumer Consum
  • git常用命令及免密登录

    常用命令 git config global user name 用户名 设置用户签名 git config global user email 邮箱 设置用户签名 git init 初始化本地库 git status 查看本地库状态 gi
  • 多线程写图像文件的一点小测试(Boost + Gual)

    转载自 http blog csdn net liminlu0314 article details 7420484 在处理遥感图像中 发现往往比较耗时的是在数据的IO中 尤其是在O 写入 的时候更加耗时 GDAL可以支持图像的多线程写入
  • 蓝桥杯——修改数组

    问题描述 给定一个长度为N的数组A A1 A2 AN 数组中有可能有重复出现的整数 在小明要按以下方法将其修改为没有重复整数的数组 小明会依次修改A2 A3 AN 当修改Ai时 小明会检查Ai是否在A1 Ai 1中出现过 如果出现过 则小明
  • C#中的事件和委托_札记1

    C 中的事件和委托 札记1 委托 自定义委托 静态方法 被委托 委托是一种类型 所以任何定义类的地方都可以定义委托类型 自定义委托的基本格式示例如下
  • RobotFramework 安装教程

    动化测试框架 具盘点 安装步骤 页面介绍 标准库 不需要安装 直接 RF 带 扩展库 快捷键 实战 RobotFramework 安装教程 动化测试框架 具盘点 java junit和testng 具 postmen newman git
  • html动态爱心代码【二】(附源码)

    目录 前言 效果演示 内容修改 完整代码 总结 前言 七夕马上就要到了 为了帮助大家高效表白 下面再给大家带来了实用的HTML浪漫表白代码 附源码 背景音乐 可用于520 情人节 生日 表白等场景 可直接使用 效果演示 内容修改 文案 di
  • go - flag包(处理命令行参数小能手)

    前言 在golang中有很多方法来处理命令行参数 简单情况下可以不使用任何库 直接使用os Args 但是golang标准库提供了flag包来专门处理命令行参数 当然还有第三方提供的处理命令行参数的库cobra cli可以参考 flag包绑
  • qt没有mysql驱动的解决办法

    qt没有mysql驱动的解决办法 第一部分 qtcreator上没有mysql驱动的解决办法 第一步 找到你的qt的版本的源码src 第二步 点击mysql pro 电脑会自动打开qtcreater 然后就是进行编译器的选择 我选择的是 在
  • BootStrap的使用

    是别人帮我们已经写好的css样式 我们如果想要使用这个BootStrap 下载BootStrap 使用 在页面上引入BootStrap 自定置 先在网上下载好BootStrap 并导入到Pycharm 引入BootStrap 注意引入的是
  • 【react】文本内容超过一行,显示为单行省略,并且出现icon图标;点击此图标,可以进行展开或收起文本功能实现

    需求 多条数据展示 每条数据的文本内容不超过一行 文本内容为一行时 不显示 展开收起icon图标 文本超过一行时 内容单行省略 并且显示 点击图标 图标切换为收起按钮 后端返回数据 const data name 测试测试测试 time 2
  • BinaryViewer(二进制查看器)使用教程(附下载)

    1 BinaryViewer操作界面 2 面板功能 1 数据面板 此面板占据了屏幕的最中央部分 其目的是顺序显示打开的文件或物理驱动器中的所有数据 此面板通常以两列显示数据 每列都可以按用户选择的格式显示数据 请转到数据显示模式 查看如何更
  • 对indexedDB的一些使用方法

    indexedDB的使用 1 打开数据库和创建数据仓库 createDB function dbName version tableName key cursor callBack 参数为 dbName数据库名 version版本号 tab
  • Python运维开发工程师养成记(while循环语句)

    图示 案例 contine和break用法 无限循环 while else语句 今天分享到这里 喜欢的盆友可以关注一下博主 链接 https ke qq com course 4300856 tuin d8aedf68
  • android 环信集成,Android 环信集成使用总结

    最近因为项目需要 需要集成环信 对于一些账号的注册 配置的添加官方文档上写的都有 就不在记录 就记录一下集成过程中遇到的问题 环信demo中的代码太乱 而且一些功能用不到 我们就移值些自己有用的放到自己的项目中 1 消息监听 环信在收到消息
  • mysql如何查询成绩前5名_sql 语句查询 前5名后5名的成绩

    蝴蝶不菲 两种办法 分别求最大和最小 然后union allselect from select from table order by 成绩 where rownum lt 5union allselect from select fro
  • 每隔5分钟输出最近一小时内点击量最多的前N个商品(SQL实现版)

    代码 package com zjc flow analysis hotitems analysis import org apache flink api common serialization SimpleStringSchema i
  • 智能合约调试指南

    不像你在其他地方看到的纸质合约 以太坊的智能合约是代码组成的 需要你以非常谨慎的态度去对待它 这是一件好事 想象下如果现实世界的合同需要编译的话会更清晰么 如果我们的合同没有被正确的编码出来 我们的交易可能会失败 导致以太币的损失 以 ga