Solidity笔记,使用Remix

Solidity笔记
pragma solidity >0.4.22;  # 版本要求大于等于0.4.22, 
# ^0.4.22的含义是大于0.4.22 但是小于一个大版本 0.5, 表示在这个范围内
contract Car {
	# solidity中的公共的成员变量,如果没有,编译器自动生成get方法,返回price
	uint public price;
	string brand;
	function setPrice(uint newPrice) public {
		price = newPrice;
	}
}
# pure 关键字代表为纯计算
 function pureAdd(uint a, uint b) public pure returns(uint sum, uint origin_a) {
        return (a+b, a);
    }
  • bytes32 类型数据 ,是指32字节数据

  • constructor() 方法在 编译器版本 0.4.22 之后出现

  • pragma声明行尾少写 ; 就会导致如下错误

ParserError: Expected pragma, import directive or contract/interface/library definition.

  • Solidity 数据类型
    enum 枚举类型,定义的时候,枚举对象必须有一个数据,不能空定义。其次,枚举类型其中元素可以被显示转换成uint类型数据,对应枚举元素在枚举对象中的下标。
uint[] memory a = new uint[](len);
// 表示定义一个无符号int数组,长度为len  
bytes memory b = new bytes(len); 
// 定义一个动态长度的字符, 存储在内存中
// memory中,数组的长度最好在定义时指定。

Struct 结构体数据类型中可以包含其他各种数据类型元素,但是不能包含该结构体自己的元素类型,因为可能会出现循环引用。可以包含数组类型的该类结构体数组,因为数组是引用类型数据,变量中存储的是指针,就不会出现循环引用问题。

// 当合约中成员变量的访问为 public 则合约自动为该变量生成获得该变量的方法
pragma solidity ^0.4.0;

contract C {
    mapping(address => uint) public balances;
    function update(uint amount) public {
        balances[msg.sender] = amount;
    }
    // function getBalance(address _addr) public view returns(uint) {
    //     return balances[_addr];
    // }   
}

contract D {   
    function fun() public returns(uint) {
        C con = new C();
        con.update(10);
        // return con.getBalance(address(con)); // "0": "uint256: 0"
        // return con.getBalance(address(this));// "0": "uint256: 10"
        return con.balances(address(this)); 
        // 可直接使用该方式调用public成员
    }
}
数据存储位置(Data location)概念

1.1 storage, memory, calldata, stack区分
在 Solidity 中,有两个地方可以存储变量:存储(storage)以及内存(memory)。Storage 变量是指永久存储在区块链中的变量。
Memory 变量则是临时的,当外部函数对某合约调用完成时,内存型变量即被移除。
内存(memory)位置还包含2种类型的存储数据位置,一种是calldata,一种是栈(stack)。

(1)calldata
这是一块只读的,且不会永久存储的位置,用来存储函数参数。 外部函数的参数(非返回参数)的数据位置被强制指定为 calldata ,效果跟 memory 差不多。
(2) 栈(stack)

另外,EVM是一个基于栈的语言,栈实际是在内存(memory)的一个数据结构,每个栈元素占为256位,栈最大长度为1024。 值类型的局部变量是存储在栈上。

不同存储的消耗(gas消耗)是不一样的,说明如下:

1.storage 会永久保存合约状态变量,开销最大;
2.memory 仅保存临时变量,函数调用之后释放,开销很小;
3. stack 保存很小的局部变量,免费使用,但有数量限制(16个变量);
4.calldata的数据包含消息体的数据,其计算需要增加n*68的GAS费用;

  • storage 存储结构是在合约创建的时候就确定好了的,它取决于合约所声明状态变量。但是内容可以被(交易)调用改变。
  • Solidity 称这个为状态改变,这也是合约级变量称为状态变量的原因。也可以更好的理解为什么状态变量都是storage存储。
  • memory 只能用于函数内部,memory 声明用来告知EVM在运行时创建一块(固定大小)内存区域给变量使用。
  • storage 在区块链中是用key/value的形式存储,而memory则表现为字节数组

solidity中设置为memory和storage临时变量的区别

  • storage 的数据在链上存储,比较贵。
    memory 的数据在内存中存储,不占用链上空间(即不能永久存库数据),比较便宜。

特例: 引用数据类型的局部变量存储在Storage中,内存无法实现存储引用数据分配无限扩展的动态空间。

  • 特殊情况
pragma solidity ^0.4.12;

contract C {
    uint public a;  // a 每次在fun函数调用一次都会自加1
    // 原因是fun函数中变长数组抢占了合约开始的存储地址,并在空间中存储了数组长度值
    uint public b;
    uint[] public data;
    function fun() public {
    	// 局部状态函数存储在合约存储的第一个位置和 全局状态变量a位置重合
    	// x的存储是在存储地址中放变长数组的长度值		
		//查找x中某一元素是利用元素下标和内存地址求哈希,得到真正存储元素的地址。
		// 改进方法, 在定义局部Storage变量时赋值,如uint[] x = data;
        uint[] x; 
        x.push(12);
        data = x;
    }
}
  • 注意点

合约中函数的参数中有存储在 storage 中的变量,函数声明必须是内部internal,public 声明会出现不可控的外界大量调用函数存储 storage 数据。

不同存储空间数据的赋值
  • memory 中数据赋值给 storage 中数据:将数据完全拷贝复制到 storage 中
  • 在局部函数中,将 storage 局部变量赋值给 storage 状态变量:将数据完全拷贝赋值
  • 将 storage 状态变量赋值给 storage 局部变量:传递的是数据引用地址

在这里插入图片描述这里产生错误的原因:y 是已经用来存储 storage 中数据的指针, 再次用 memory 中数据赋值就会产生冲突。

  • 例2
contract GuessGame{
    address public owner = msg.sender;
    uint public luckNum = 52;
    struct Guess{
        address guesser;
        uint guessNum;
    }
    Guess[] public guessHistory;
    constructor () public payable{}
    function guess(uint _guessNum) public payable{
        Guess storage newGuess;  // 实际测试:newGuess居然不是指针地址???
        newGuess.guesser = msg.sender;
        newGuess.guessNum = _guessNum;
        guessHistory.push(newGuess);
        if (_guessNum == luckNum) {
            msg.sender.transfer(msg.value * 2);
        }
    }
    function () public payable{} //?? 没有这个也转账成功了
}

实际情况:luckNum与guess中的数据一致,且随guess的不断变化而变化

在这里插入图片描述

分析:newGuess的数据分别占据了owner 和 luckNum 的 storage存储地址

contract GuessGame{
    // address public owner = msg.sender;
    uint public luckNum = 52;
    struct Guess{
        address guesser;
        uint guessNum;
    }
    Guess[] public guessHistory;
    constructor () public payable{}
    function guess(uint _guessNum) public payable{
        Guess storage newGuess; ## 这里就是指针地址,并将40位16进制地址放到luckNum上
        ## ?? 疑问:为啥每次guess调用,luckNum没有跟随变化??
        newGuess.guesser = msg.sender;
        newGuess.guessNum = _guessNum;
        guessHistory.push(newGuess);
        if (_guessNum == luckNum) {
            msg.sender.transfer(msg.value * 2);
        }
    }
    function () public payable{}
}

# 此时luckNum 在第一次guess之后为:
11544 14090 61981 17968 18182 30213 94152 80051 21425 0812
函数的类型
  • 值类型 :internal 和 external
状态变量
  • 状态变量的可见性不能设置external,默认是internal,public 的作用是为状态变量添加了同名状态变量的函数,用于外部读取

  • 各个类型的区别

  • public 和 external:external 限制为仅外部访问,内部也不能访问,public 内部外部都可以访问。

  • private 和 internal:private限制更严,internal为内部访问,且派生合约也可以访问,private定义的函数和状态变量仅在当前定义的合约内部使用。

  • 在部署合约的同时要想给合约地址转钱,就必须在constructor 函数上加上payable

问题

使用最新版本geth客户,当执行personal.unlockAccount()或在程序中调用personal_unlockAccount接口时,会出现:account unlock with HTTP access is forbidden异常。
新版本geth,出于安全考虑,默认禁止了HTTP通道解锁账户,相关issue:https://github.com/ethereum/go-ethereum/pull/17037
解决方案
如果已经了解打开此功能的风险,可通启动命令中添加参数:
–allow-insecure-unlock

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值