<<WTF-Solidity>>学习笔记(part 9-12)

part 9 

这一讲,我们介绍Solidity中和常量相关的两个关键字,constant(常量)和immutable(不变量)。状态变量声明这两个关键字之后,不能在初始化后更改数值。这样做的好处是提升合约的安全性并节省gas

另外,只有数值变量可以声明constantimmutablestringbytes可以声明为constant,但不能为immutable

constant和immutable

constant

constant变量必须在声明的时候初始化,之后再也不能改变。尝试改变的话,编译不通过。

// constant变量必须在声明的时候初始化,之后不能改变
uint256 constant CONSTANT_NUM = 10;
string constant CONSTANT_STRING = "0xAA";
bytes constant CONSTANT_BYTES = "WTF";
address constant CONSTANT_ADDRESS = 0x0000000000000000000000000000000000000000;

immutable

immutable变量可以在声明时或构造函数中初始化,因此更加灵活。在Solidity v0.8.21以后,immutable变量不需要显式初始化,未显式初始化的immutable变量将使用数值类型的初始值(见 8. 变量初始值)。反之,则需要显式初始化。 若immutable变量既在声明时初始化,又在constructor中初始化,会使用constructor初始化的值。

part 10:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract InsertionSort {
    // if else
    function ifElseTest(uint256 _number) public pure returns(bool){
        if(_number == 0){
            return(true);
        }else{
            return(false);
        }
    }

    // for loop
    function forLoopTest() public pure returns(uint256){
        uint sum = 0;
        for(uint i = 0; i < 10; i++){
            sum += i;
        }
        return(sum);
    }

    // while
    function whileTest() public pure returns(uint256){
        uint sum = 0;
        uint i = 0;
        while(i < 10){
            sum += i;
            i++;
        }
        return(sum);
    }

    // do-while
    function doWhileTest() public pure returns(uint256){
        uint sum = 0;
        uint i = 0;
        do{
            sum += i;
            i++;
        }while(i < 10);
        return(sum);
    }

    // 三元运算符 ternary/conditional operator
    function ternaryTest(uint256 x, uint256 y) public pure returns(uint256){
        // return the max of x and y
        return x >= y ? x: y; 
    }


    // 插入排序 错误版
    function insertionSortWrong(uint[] memory a) public pure returns(uint[] memory) {
        for (uint i = 1;i < a.length;i++){
            uint temp = a[i];
            uint j=i-1;
            while( (j >= 0) && (temp < a[j])){
                a[j+1] = a[j];
                j--;
            }
            a[j+1] = temp;
        }
        return(a);
    }

    // 插入排序 正确版
    function insertionSort(uint[] memory a) public pure returns(uint[] memory) {
        // note that uint can not take negative value
        for (uint i = 1;i < a.length;i++){
            uint temp = a[i];
            uint j=i;
            while( (j >= 1) && (temp < a[j-1])){
                a[j] = a[j-1];
                j--;
            }
            a[j] = temp;
        }
        return(a);
    }
}

part  11 :

构造函数

构造函数(constructor)是一种特殊的函数,每个合约可以定义一个,并在部署合约的时候自动运行一次。它可以用来初始化合约的一些参数,例如初始化合约的owner地址:

address owner; // 定义owner变量

// 构造函数
constructor(address initialOwner) {
    owner = initialOwner; // 在部署合约的时候,将owner设置为传入的initialOwner地址
}

注意:构造函数在不同的Solidity版本中的语法并不一致,在Solidity 0.4.22之前,构造函数不使用 constructor 而是使用与合约名同名的函数作为构造函数而使用,由于这种旧写法容易使开发者在书写时发生疏漏(例如合约名叫 Parents,构造函数名写成 parents),使得构造函数变成普通函数,引发漏洞,所以0.4.22版本及之后,采用了全新的 constructor 写法。

修饰器

修饰器(modifier)是Solidity特有的语法,类似于面向对象编程中的装饰器(decorator),声明函数拥有的特性,并减少代码冗余。它就像钢铁侠的智能盔甲,穿上它的函数会带有某些特定的行为。modifier的主要使用场景是运行函数前的检查,例如地址,变量,余额等。

part 12:

这一讲,我们用转账ERC20代币为例来介绍Solidity中的事件(event)。

事件

Solidity中的事件(event)是EVM上日志的抽象,它具有两个特点:

  • 响应:应用程序(ethers.js)可以通过RPC接口订阅和监听这些事件,并在前端做响应。
  • 经济:事件是EVM上比较经济的存储数据的方式,每个大概消耗2,000 gas;相比之下,链上存储一个新变量至少需要20,000 gas

声明事件

事件的声明由event关键字开头,接着是事件名称,括号里面写好事件需要记录的变量类型和变量名。以ERC20代币合约的Transfer事件为例:

event Transfer(address indexed from, address indexed to, uint256 value);

我们可以看到,Transfer事件共记录了3个变量fromtovalue,分别对应代币的转账地址,接收地址和转账数量,其中fromto前面带有indexed关键字,他们会保存在以太坊虚拟机日志的topics中,方便之后检索。

释放事件

我们可以在函数里释放事件。在下面的例子中,每次用_transfer()函数进行转账操作的时候,都会释放Transfer事件,并记录相应的变量。

// 定义_transfer函数,执行转账逻辑
function _transfer(
    address from,
    address to,
    uint256 amount
) external {

    _balances[from] = 10000000; // 给转账地址一些初始代币

    _balances[from] -=  amount; // from地址减去转账数量
    _balances[to] += amount; // to地址加上转账数量

    // 释放事件
    emit Transfer(from, to, amount);

Event.sol 在remix上的测试结果

index.html是我的前端页面:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>儿童肠梗阻医疗辅助诊断系统</title> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <header> <div class="header-content"> <h1>儿童肠梗阻医疗辅助诊断系统</h1> <div class="contact-info"> <span>400-XXX-XXXX</span> </div> </div> </header> <div class="login-form"> <h2>账号登录</h2> <form action="/login" method="post"> <div class="form-group"> <!-- <label for="username">用户名/邮箱/工号/学号</label> --> <input type="text" id="userid" name="userid" placeholder="请输入账号" required> </div> <div class="form-group"> <!-- <label for="password">密码</label> --> <input type="password" id="password" name="password" placeholder="请输入密码" required> </div> <div class="form-group"> <button type="submit">登录</button> </div> <div class="form-group"> <a href="#">忘记密码?</a> </div> <div class="agreement"> <input type="checkbox" id="agree" required> <label for="agree">我已阅读并同意 <a href="#">《服务条款》</a> 和 <a href="#">《隐私政策》</a></label> </div> </form> </div> </body> </html> # 登录路由 @app.route(&#39;/login&#39;, methods=[&#39;POST&#39;]) def login(): userid = request.form.get(&#39;userid&#39;) password = request.form.get(&#39;password&#39;) ... 这是我用flask处理登录逻辑的代码,要求获得userid和password之后,通过userid查询数据库对应的password,正确则跳转页面到home.html
03-14
<think>嗯,用户想在Flask应用中实现基于数据库验证的用户登录逻辑,具体是如何通过userid查询数据库中的密码来完成验证,并重定向到home.html。首先,我需要回忆一下Flask处理登录的基本流程。用户提交表单,后端获取userid和密码,然后查询数据库,比对密码,如果正确就登录成功,重定向到主页。 首先,可能需要用到Flask-WTF来处理表单,定义表单类,包含userid和password字段。然后,在路由函数里处理POST请求,获取表单数据。接着,连接数据库,比如使用SQLAlchemy或者直接使用SQLite3的模块。查询用户是否存在,这里要注意SQL注入的问题,所以应该使用参数化查询,而不是拼接字符串。 然后,密码不能明文存储,所以需要检查存储的密码是否是哈希过的。比如使用werkzeug的generate_password_hash和check_password_hash函数。在注册的时候,密码会被哈希后存储,登录时就需要用check_password_hash来比对用户输入的密码和数据库中的哈希值。 接下来,如果验证成功,应该使用Flask-Login的login_user函数来记录用户登录状态,然后重定向到home.html。如果失败,要返回错误信息。 可能还需要考虑数据库模型的设计,比如User表的结构,是否有userid和password_hash字段。另外,需要初始化Flask-Login,设置user_loader回调,这样Flask-Login才能正确加载用户。 可能遇到的问题包括数据库连接失败,查询结果为空,或者密码哈希比对不正确。需要处理这些异常情况,给用户友好的提示。另外,重定向的时候要确保home.html的路由正确,并且有对应的视图函数。 还有,记得在登录成功后设置session,这样用户才能保持登录状态。同时,需要保护路由,比如home.html必须登录后才能访问,可以使用@login_required装饰器。 现在,把这些步骤整理成代码示例,确保每一步都正确。比如,先定义User模型,使用SQLAlchemy,然后定义登录表单,处理路由,查询数据库,验证密码,登录用户,重定向。同时,给出相关的扩展库的安装和配置步骤,比如Flask-WTF、Flask-SQLAlchemy、Flask-Login等。 最后,生成相关问题,可能涉及密码安全、Flask-Login的其他功能、数据库迁移或者记住用户登录状态等。这些问题可以帮助用户进一步深入学习和完善他们的应用。</think>在Flask中实现基于数据库的用户登录验证需要结合表单处理、数据库查询和密码验证三个关键步骤,以下是具体实现流程: ### 一、配置基础环境 1. 安装必要扩展 ```bash pip install flask flask-wtf flask-sqlalchemy flask-login ``` 2. 初始化应用(app.py) ```python from flask import Flask, render_template, redirect, url_for from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager, UserMixin, login_user app = Flask(__name__) app.config[&#39;SECRET_KEY&#39;] = &#39;your-secret-key&#39; app.config[&#39;SQLALCHEMY_DATABASE_URI&#39;] = &#39;sqlite:///users.db&#39; db = SQLAlchemy(app) login_manager = LoginManager(app) ``` ### 二、定义数据模型 ```python class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) userid = db.Column(db.String(80), unique=True) password_hash = db.Column(db.String(128)) def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) ``` ### 三、创建登录表单 ```python from flask_wtf import FlaskForm from wtforms import StringField, PasswordField from wtforms.validators import DataRequired class LoginForm(FlaskForm): userid = StringField(&#39;用户ID&#39;, validators=[DataRequired()]) password = PasswordField(&#39;密码&#39;, validators=[DataRequired()]) ``` ### 四、实现登录路由 ```python @app.route(&#39;/login&#39;, methods=[&#39;GET&#39;, &#39;POST&#39;]) def login(): form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(userid=form.userid.data).first() if user and user.check_password(form.password.data): login_user(user) return redirect(url_for(&#39;home&#39;)) return &#39;无效的用户名或密码&#39; return render_template(&#39;login.html&#39;, form=form) ``` ### 五、用户加载器 ```python @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) ``` ### 六、创建模板(login.html) ```html <form method="POST"> {{ form.hidden_tag() }} {{ form.userid.label }} {{ form.userid() }} {{ form.password.label }} {{ form.password() }} <button type="submit">登录</button> </form> ``` 关键点说明: 1. 使用`filter_by`进行数据库查询时,会自动参数化输入值,防止SQL注入[^1] 2. `check_password`方法通过`werkzeug.security`的哈希验证保证密码安全 3. `login_user`会设置用户会话,实现状态保持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值