EIP55-以太坊账户地址校验算法
以太坊账户地址是一个40字符的hex string,细心的朋友可能会发现账户地址有些字母包含了大写字母和小写字母,但实际交易时,用小些地址也可以正常执行操作。
查了一下资料,发现原来包含大写字母的是经过了校验和(checksum)的地址
0x7cb57b5a97eabe94205c07890be4c1ad31e486a8
0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8
上面两个地址的区别是,前者不包含校验和(checksum),后者是包含校验和的地址。这个校验和有什么作用呢?因为以太坊地址本身不包含校验信息,一旦用户输入错误,没有一种机制校验,该交易将会永远丢失。
为了解决上面的问题,2016年有人在github提了一个Improvement Proposal《Yet another cool checksum address encoding》,用一种向后兼容的校验和算法对账户地址进行checksum。这种算法得满足下面条件:
- 向下兼容
- 保持账户地址的长度和信息都不发生变化
- 足够低的碰撞概率
上面提到的proposal提出了一种算法来解决这个问题,该算法原文描述如下:
In English, convert the address to hex, but if the ith digit is a letter (ie. it’s one of
abcdef
) print it in uppercase if the ith bit of the hash of the address (in binary form) is 1 otherwise print it in lowercase.
上面描述的意思需要结合代码看才更加清晰,我用自己的语言描述如下:
先把账户地址经过digist再转为hex string,然后遍历用户原本的地址,如果hex string和原地址对应位置的字符是一个字母时,把原地址的字符转为大写字符,否则是一个小些字符。
该算法最终被采纳为以太坊的标准,EIP-55。EIP-55的实现原理实际上非常简单,代码甚至都不到10行,非常简洁。下面是EIP55的JS实现,感受一下极致简洁的代码带来的美感。
const createKeccakHash = require('keccak')
function toChecksumAddress (address) {
address = address.toLowerCase().replace('0x', '')
var hash = createKeccakHash('keccak256').update(address).digest('hex')
var ret = '0x'
for (var i = 0; i < address.length; i++) {
if (parseInt(hash[i], 16) > 7) {
ret += address[i].toUpperCase()
} else {
ret += address[i]
}
}
return ret
}
参考资料:
Vitalik Buterin, Alex Van de Sande, “EIP-55: Mixed-case checksum address encoding,” Ethereum Improvement Proposals, no. 55, January 2016. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-55.
Ethereum Address Has Uppercase and Lowercase Letters
shadowsocks rc4-md5算法介绍
shadowsocks协议早期使用RC4加密算法用于加密数据,不过因为每次数据都适用同一个密钥流加密,存在很大的安全隐患,后面更新了RC4-MD5算法。
即便关于RC4-MD5协议的资料很难找到,而且RC4-MD5算法现在也不在安全,但还是有必要介绍一遍,因为它简单,很便于理解,在后期也会展示RC4算法的缺陷。
RC4-MD5本质上还是RC4对数据进行加密。MD5的意思是对key进行MD5运算,得到RC4的key,最终会被RC4算法用于生成密钥流。简单来说,用户的密码会经过下面步骤转换为RC4的keystream
- k1 = toBytes(password)
- k2 = ssKey(k1)
- rc4-md5-key = md5(toBytes(k2,iv))
手把手使用Java实现一个Socks5代理
1. 前言
上一篇文章介绍了socks5协议的工作过程和协议的细节,通过上一篇文章我们可以认识到socks5协议主要有3个阶段,分别为: 协商、请求,转发(Relay)。本文将手把手使用Java语言实现一个简单的socks5代理
特别提醒: 本文目的仅作为加深socks5协议理解,其中的代码并不是严谨的代码,也没考虑其他的情况。在实际的开发过程中,需要考虑更多的意外情况。
上一篇文章中有一张时序图展示了socks5的大概工作过程,本文将使用Java把这些过程一一实现。
理解socks5协议的工作过程和协议细节
1. 前言
本位将由浅入深带大家详细了解socks5协议。文章首先会对socks协议进行简单介绍,接着会介绍socks5协议的使用场景,然后介绍它的工作工程,最后介绍协议的细节(握手、数据转发)。
2. 协议介绍
2.1 什么是socks协议
啥是socks协议呢? 这里贴一段维基百科对它的定义
SOCKS is an Internet protocol that exchanges network packets between a client and server through a proxy server
大概的意思是: socks是一种互联网协议,它通过一个代理服务器在客户端和服务端之间交换网络数据。简单来说,它就是一种代理协议,扮演一个中间人的角色,在客户端和目标主机之间转发数据。
socks协议位于OSI模型中的第五层,即会话层(Session Layer)。
理解Java中的Bridge Method
bridge method又叫synthetic method,它是由Java编译器自动生成的一个合成方法,这个方法不会出现在源码中,也不能显式调用。我们先通过一个例子对bridge method有一个感性的认识。
// Code 1-1
class Animal {
public Animal getAnimal() {
return new Animal();
}
}
class Dog extends Animal {
public Dog getAnimal() {
return new Dog();
}
}
上面定义了Animal和Dog两个类,Dog是Animal的subclass,且Dog类override了Animal的getAnimal
方法。如果你现在尝试去编译Dog,很大概率是可以直接通过编译,不过当我们尝试使用JDK 1.4以下版本编译,就是另外一回事了。
javac org/wiyi/bridge/Dog.java
org/wiyi/bridge/Dog.java:6: getAnimal() in org.wiyi.bridge.Dog cannot override g
etAnimal() in org.wiyi.bridge.Animal; attempting to use incompatible return type
found : org.wiyi.bridge.Dog
required: org.wiyi.bridge.Animal
public Dog getAnimal() {
^
1 error
深入理解面向对象中的多态
多态
多态并非是计算机科学领域的专有名词,在其他领域比如生物领域也有使用。比如猫科动物中,有狮子、老虎、豹、山猫等,其中雄性狮子会有很长的胡须等,都是现实生活中的多态。
基于上面的描述,我们不难理解计算机领域中的”多态”,实际上它也是对现实世界的一种抽象。在现实中,猫可以代指小猫咪、老虎、狮子等动物,具体到计算机领域的抽象,可以用下面语法表达:
Cat cat = new Tiger();
用一个符号来表现同一种类型的不同形态,这就是多态。相信所有Java程序员对上面语法早已烂熟于心,这种语法属于多态中的一个分支,叫Subtyping(也叫subtype polymorphism,子类型多态)。
多态是类型系统中一个重要的概念,在Java语言中,实现了3种类型的多态,分别为:
- Ad hoc polymorphism (特殊的多态)
- subtype polymorphism (子类型多态)
- Parametric polymorphism (参数化多态)
本文将详细介绍这3种类型的多态,进一步加深对多态的理解。
理解Mysql InnoDB引擎中的锁
Mysql是支持ACID特性的数据库。我们都知道”C”代表Consistent,当不同事务操作同一行记录时,为了保证一致性,需要对记录加锁。在Mysql中,不同的引擎下的锁行为也会不同,本文将重点介绍Mysql InnoDB引擎中常见的锁。
准备
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`age` tinyint(4) DEFAULT '0',
`phone` varchar(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
#插入基础数据
INSERT INTO `user` (`id`, `name`, `age`, `phone`)
VALUES
(1, '张三', 18, '13800138000'),
(2, '李四', 20, '13800138001'),
(3, '王五', 22, '13800138002'),
(4, '赵六', 26, '13800138003'),
(5, '孙七', 30, '13800138004');
为了方便讲解,创建一张user表,设置age的字段为普通索引,并填充以下数据。本文所有的sql语句均基于这张表。
id | name | age | Phone |
---|---|---|---|
1 | 张三 | 18 | 13800138000 |
2 | 李四 | 20 | 13800138001 |
3 | 王五 | 22 | 13800138002 |
4 | 赵六 | 26 | 13800138003 |
5 | 孙七 | 30 | 13800138004 |
带你彻底理解Linux五种I/O模型
在编程中I/O是必不可少的操作。日常中,我们最常接触的就是Blocking I/O,也就是常说的阻塞I/O。相信你也听过同步非阻塞I/O(Non-Blocking I/O),异步I/O(Asynchronous I/O)。
要理解这些I/O模型并不是一件容易的事,相信你也在网上看到许多人尝试对这些概念解释,不过我认为目前并没有什么文章能对它们解释的比较清晰的文章。尤其是很多一知半解的人胡乱举例,更是加深理解难度。
知识准备
阅读本文之前,希望读者具备以下的知识,否则看了也是一脸懵。
- 了解你所知语言中的基本I/O操作
- User space & Kernel space 的概念
- 理解File Descriptor。如果不懂,请移步理解linux中的file descriptor(文件描述符)。建议读者不管对file descriptor有没有了解,都去阅读这篇文章,对你理解I/O模型会有非常大的帮助。
看完了上面的内容,希望读者已经理解了下面内容
- 程序无法直接访问硬件资源,对磁盘(硬件)的读写需要发起system call,让kernel处理。
- 大部分和I/O有关的system call,都需要把fd(file descriptor)作为参数传递给kernel。