Compare commits

..

No commits in common. 'main' and 'cunchu_zc' have entirely different histories.

25
node_modules/body-parser/index.js generated vendored

@ -90,37 +90,28 @@ Object.defineProperty(exports, 'urlencoded', {
* @public
*/
// 定义一个 bodyParser 函数,接受一个可选的配置选项对象
function bodyParser(options) {
// 创建一个对象 opts继承自 options 对象。 如果没有传递 options使用 null。
// 在 opts 对象中,定义一个名为 'type' 的属性,默认值为 undefined允许修改其值。
function bodyParser (options) {
// use default type for parsers
var opts = Object.create(options || null, {
type: {
configurable: true, // 该属性可以被重新定义
enumerable: true, // 该属性会出现在枚举操作中(如 for...in
value: undefined, // 默认值为 undefined
writable: true // 该属性的值可以被修改
configurable: true,
enumerable: true,
value: undefined,
writable: true
}
})
// 调用 exports 中的 urlencoded 和 json 函数,分别返回解析请求体为 URL 编码格式和 JSON 格式的中间件
var _urlencoded = exports.urlencoded(opts)
var _json = exports.json(opts)
// 返回一个新的中间件函数,接收 req, res 和 next 作为参数
return function bodyParser(req, res, next) {
// 首先调用 _json 中间件解析请求体为 JSON 格式
_json(req, res, function(err) {
// 如果解析 JSON 时出错,调用 next(err) 抛出错误
return function bodyParser (req, res, next) {
_json(req, res, function (err) {
if (err) return next(err)
// 如果 JSON 解析没有问题,则继续调用 _urlencoded 中间件解析 URL 编码格式
_urlencoded(req, res, next)
})
}
}
/**
* Create a getter for loading a parser.
* @private

272
node_modules/depd/index.js generated vendored

@ -365,249 +365,137 @@ function getStack() {
* Capture call site stack from v8.
*/
/**
* 准备堆栈跟踪返回传入的堆栈
* @param {Object} obj - 捕获堆栈的对象
* @param {Array} stack - 堆栈数组
* @returns {Array} - 返回原始堆栈数组
*/
function prepareObjectStackTrace(obj, stack) {
return stack; // 直接返回传入的堆栈数组
function prepareObjectStackTrace (obj, stack) {
return stack
}
/**
* 返回一个包装的函数并添加弃用消息
* @param {Function} fn - 要包装的函数
* @param {string} message - 弃用消息
* @returns {Function} - 包装后的函数
* Return a wrapped function in a deprecation message.
*/
function wrapfunction(fn, message) {
// 检查传入的参数是否为函数
function wrapfunction (fn, message) {
if (typeof fn !== 'function') {
throw new TypeError('argument fn must be a function'); // 如果不是,抛出类型错误
throw new TypeError('argument fn must be a function')
}
var args = createArgumentsString(fn.length); // 创建函数参数字符串
var stack = getStack(); // 获取当前堆栈
var site = callSiteLocation(stack[1]); // 获取调用位置
var args = createArgumentsString(fn.length)
var stack = getStack()
var site = callSiteLocation(stack[1])
site.name = fn.name; // 设置调用位置的名称为函数名
site.name = fn.name
// 使用新函数创建一个包装函数
// eslint-disable-next-line no-new-func
var deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site',
'"use strict"\n' +
'return function (' + args + ') {' +
'log.call(deprecate, message, site)\n' + // 调用日志记录弃用消息
'return fn.apply(this, arguments)\n' + // 调用原始函数并传递参数
'}')(fn, log, this, message, site); // 立即调用新函数并传入参数
'"use strict"\n' +
'return function (' + args + ') {' +
'log.call(deprecate, message, site)\n' +
'return fn.apply(this, arguments)\n' +
'}')(fn, log, this, message, site)
return deprecatedfn; // 返回包装后的函数
return deprecatedfn
}
/**
* 在属性上添加弃用消息的包装
* @param {Object} obj - 拥有属性的对象
* @param {string} prop - 属性名称
* @param {string} message - 弃用消息
* Wrap property in a deprecation message.
*/
function wrapproperty(obj, prop, message) {
// 检查传入的对象是否有效
function wrapproperty (obj, prop, message) {
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
throw new TypeError('argument obj must be object'); // 如果无效,抛出类型错误
throw new TypeError('argument obj must be object')
}
var descriptor = Object.getOwnPropertyDescriptor(obj, prop); // 获取属性描述符
var descriptor = Object.getOwnPropertyDescriptor(obj, prop)
// 检查属性是否存在
if (!descriptor) {
throw new TypeError('must call property on owner object'); // 如果不存在,抛出类型错误
throw new TypeError('must call property on owner object')
}
// 检查属性是否可配置
if (!descriptor.configurable) {
throw new TypeError('property must be configurable'); // 如果不可配置,抛出类型错误
throw new TypeError('property must be configurable')
}
var deprecate = this; // 保存当前上下文
var stack = getStack(); // 获取当前堆栈
var site = callSiteLocation(stack[1]); // 获取调用位置
var deprecate = this
var stack = getStack()
var site = callSiteLocation(stack[1])
// 设置调用位置的名称为属性名称
site.name = prop;
// set site name
site.name = prop
// 如果是数据描述符,则转换为访问器描述符
// convert data descriptor
if ('value' in descriptor) {
descriptor = convertDataDescriptorToAccessor(obj, prop, message);
descriptor = convertDataDescriptorToAccessor(obj, prop, message)
}
var get = descriptor.get; // 获取 getter
var set = descriptor.set; // 获取 setter
var get = descriptor.get
var set = descriptor.set
// 包装 getter
// wrap getter
if (typeof get === 'function') {
descriptor.get = function getter() {
log.call(deprecate, message, site); // 调用日志记录弃用消息
return get.apply(this, arguments); // 调用原始 getter
};
descriptor.get = function getter () {
log.call(deprecate, message, site)
return get.apply(this, arguments)
}
}
// 包装 setter
// wrap setter
if (typeof set === 'function') {
descriptor.set = function setter() {
log.call(deprecate, message, site); // 调用日志记录弃用消息
return set.apply(this, arguments); // 调用原始 setter
};
descriptor.set = function setter () {
log.call(deprecate, message, site)
return set.apply(this, arguments)
}
}
// 重新定义属性,应用新的描述符
Object.defineProperty(obj, prop, descriptor);
Object.defineProperty(obj, prop, descriptor)
}
/**
* 创建一个弃用错误对象
* @param {string} namespace - 命名空间
* @param {string} message - 弃用消息
* @param {Array} stack - 堆栈数组
* Create DeprecationError for deprecation
*/
function DeprecationError(namespace, message, stack) {
var error = new Error(); // 创建一个新的错误对象
var stackString;
// 定义构造函数属性
function DeprecationError (namespace, message, stack) {
var error = new Error()
var stackString
Object.defineProperty(error, 'constructor', {
value: DeprecationError,
});
value: DeprecationError
})
// 定义消息属性
Object.defineProperty(error, 'message', {
configurable: true,
enumerable: false,
value: message, // 设置错误消息
writable: true,
});
}
/**
* 准备堆栈跟踪返回传入的堆栈
* @param {Object} obj - 捕获堆栈的对象
* @param {Array} stack - 堆栈数组
* @returns {Array} - 返回原始堆栈数组
*/
function prepareObjectStackTrace(obj, stack) {
return stack; // 直接返回传入的堆栈数组
}
/**
* 返回一个包装的函数并添加弃用消息
* @param {Function} fn - 要包装的函数
* @param {string} message - 弃用消息
* @returns {Function} - 包装后的函数
*/
function wrapfunction(fn, message) {
// 检查传入的参数是否为函数
if (typeof fn !== 'function') {
throw new TypeError('argument fn must be a function'); // 如果不是,抛出类型错误
}
var args = createArgumentsString(fn.length); // 创建函数参数字符串
var stack = getStack(); // 获取当前堆栈
var site = callSiteLocation(stack[1]); // 获取调用位置
site.name = fn.name; // 设置调用位置的名称为函数名
// 使用新函数创建一个包装函数
// eslint-disable-next-line no-new-func
var deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site',
'"use strict"\n' +
'return function (' + args + ') {' +
'log.call(deprecate, message, site)\n' + // 调用日志记录弃用消息
'return fn.apply(this, arguments)\n' + // 调用原始函数并传递参数
'}')(fn, log, this, message, site); // 立即调用新函数并传入参数
return deprecatedfn; // 返回包装后的函数
}
/**
* 在属性上添加弃用消息的包装
* @param {Object} obj - 拥有属性的对象
* @param {string} prop - 属性名称
* @param {string} message - 弃用消息
*/
function wrapproperty(obj, prop, message) {
// 检查传入的对象是否有效
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
throw new TypeError('argument obj must be object'); // 如果无效,抛出类型错误
}
var descriptor = Object.getOwnPropertyDescriptor(obj, prop); // 获取属性描述符
value: message,
writable: true
})
// 检查属性是否存在
if (!descriptor) {
throw new TypeError('must call property on owner object'); // 如果不存在,抛出类型错误
}
// 检查属性是否可配置
if (!descriptor.configurable) {
throw new TypeError('property must be configurable'); // 如果不可配置,抛出类型错误
}
var deprecate = this; // 保存当前上下文
var stack = getStack(); // 获取当前堆栈
var site = callSiteLocation(stack[1]); // 获取调用位置
// 设置调用位置的名称为属性名称
site.name = prop;
// 如果是数据描述符,则转换为访问器描述符
if ('value' in descriptor) {
descriptor = convertDataDescriptorToAccessor(obj, prop, message);
}
var get = descriptor.get; // 获取 getter
var set = descriptor.set; // 获取 setter
// 包装 getter
if (typeof get === 'function') {
descriptor.get = function getter() {
log.call(deprecate, message, site); // 调用日志记录弃用消息
return get.apply(this, arguments); // 调用原始 getter
};
}
// 包装 setter
if (typeof set === 'function') {
descriptor.set = function setter() {
log.call(deprecate, message, site); // 调用日志记录弃用消息
return set.apply(this, arguments); // 调用原始 setter
};
}
// 重新定义属性,应用新的描述符
Object.defineProperty(obj, prop, descriptor);
}
/**
* 创建一个弃用错误对象
* @param {string} namespace - 命名空间
* @param {string} message - 弃用消息
* @param {Array} stack - 堆栈数组
*/
function DeprecationError(namespace, message, stack) {
var error = new Error(); // 创建一个新的错误对象
var stackString;
Object.defineProperty(error, 'name', {
enumerable: false,
configurable: true,
value: 'DeprecationError',
writable: true
})
// 定义构造函数属性
Object.defineProperty(error, 'constructor', {
value: DeprecationError,
});
Object.defineProperty(error, 'namespace', {
configurable: true,
enumerable: false,
value: namespace,
writable: true
})
// 定义消息属性
Object.defineProperty(error, 'message', {
Object.defineProperty(error, 'stack', {
configurable: true,
enumerable: false,
value: message, // 设置错误消息
writable: true,
});
get: function () {
if (stackString !== undefined) {
return stackString
}
// prepare stack trace
return (stackString = createStackString.call(this, stack))
},
set: function setter (val) {
stackString = val
}
})
return error
}

233
node_modules/destroy/index.js generated vendored

@ -5,93 +5,88 @@
* MIT Licensed
*/
'use strict'; // 启用严格模式
'use strict'
/**
* 模块依赖项
* Module dependencies.
* @private
*/
var EventEmitter = require('events').EventEmitter; // 引入 EventEmitter 类
var ReadStream = require('fs').ReadStream; // 引入文件读取流
var Stream = require('stream'); // 引入流模块
var Zlib = require('zlib'); // 引入 zlib 模块,用于压缩和解压缩流
var EventEmitter = require('events').EventEmitter
var ReadStream = require('fs').ReadStream
var Stream = require('stream')
var Zlib = require('zlib')
/**
* 模块导出
* Module exports.
* @public
*/
module.exports = destroy; // 导出 destroy 函数
module.exports = destroy
/**
* 销毁给定的流并可选择抑制未来的 `error` 事件
* Destroy the given stream, and optionally suppress any future `error` events.
*
* @param {object} stream - 要销毁的流对象
* @param {boolean} suppress - 是否抑制错误事件
* @param {object} stream
* @param {boolean} suppress
* @public
*/
function destroy(stream, suppress) {
// 判断流的类型并调用相应的销毁函数
function destroy (stream, suppress) {
if (isFsReadStream(stream)) {
destroyReadStream(stream); // 如果是文件读取流,调用销毁文件读取流的函数
destroyReadStream(stream)
} else if (isZlibStream(stream)) {
destroyZlibStream(stream); // 如果是 zlib 流,调用销毁 zlib 流的函数
destroyZlibStream(stream)
} else if (hasDestroy(stream)) {
stream.destroy(); // 如果流具有 destroy 方法,则直接调用它
stream.destroy()
}
// 如果流是事件发射器并且 suppress 为 true移除所有错误事件监听器
if (isEventEmitter(stream) && suppress) {
stream.removeAllListeners('error'); // 移除所有 'error' 事件监听器
stream.addListener('error', noop); // 添加一个空的错误处理函数
stream.removeAllListeners('error')
stream.addListener('error', noop)
}
return stream; // 返回被销毁的流
return stream
}
/**
* 销毁 ReadStream
* Destroy a ReadStream.
*
* @param {object} stream - 要销毁的 ReadStream 对象
* @param {object} stream
* @private
*/
function destroyReadStream(stream) {
stream.destroy(); // 调用流的 destroy 方法
function destroyReadStream (stream) {
stream.destroy()
// 如果流具有 close 方法,添加 'open' 事件的监听器
if (typeof stream.close === 'function') {
// node.js 核心 bug 的解决方法
stream.on('open', onOpenClose); // 在流打开时调用 onOpenClose 函数
// node.js core bug work-around
stream.on('open', onOpenClose)
}
}
/**
* 关闭 Zlib
* Close a Zlib stream.
*
* Zlib 流在 Node.js 4.5.5 之前的实现中
* zlib 遇到错误时.close() 方法存在bug
* Zlib streams below Node.js 4.5.5 have a buggy implementation
* of .close() when zlib encountered an error.
*
* @param {object} stream - 要关闭的 Zlib 流对象
* @param {object} stream
* @private
*/
function closeZlibStream(stream) {
// 如果流有错误,处理流的关闭
function closeZlibStream (stream) {
if (stream._hadError === true) {
var prop = stream._binding === null
? '_binding' // 选择属性
: '_handle';
? '_binding'
: '_handle'
// 创建一个关闭函数,将属性设置为 null
stream[prop] = {
close: function () { this[prop] = null; }
};
close: function () { this[prop] = null }
}
}
stream.close(); // 调用流的 close 方法
stream.close()
}
/**
@ -110,88 +105,78 @@ function closeZlibStream(stream) {
* @private
*/
/**
* 销毁 Zlib
*
* @param {object} stream - 要销毁的 Zlib 流对象
*/
function destroyZlibStream(stream) {
// 检查流是否具有 destroy 方法
function destroyZlibStream (stream) {
if (typeof stream.destroy === 'function') {
// node.js 核心 bug 的解决方法
// node.js core bug work-around
// istanbul ignore if: node.js 0.8
if (stream._binding) {
// 处理 Node.js 版本小于 0.10.0 的情况
stream.destroy(); // 调用流的 destroy 方法
// 如果流正在处理数据
// node.js < 0.10.0
stream.destroy()
if (stream._processing) {
stream._needDrain = true; // 标记需要排空
stream.once('drain', onDrainClearBinding); // 一旦排空,调用 onDrainClearBinding 函数
stream._needDrain = true
stream.once('drain', onDrainClearBinding)
} else {
stream._binding.clear(); // 清除绑定
stream._binding.clear()
}
} else if (stream._destroy && stream._destroy !== Stream.Transform.prototype._destroy) {
// 处理 Node.js 版本大于等于 12或 11.1.0,或 10.15.1 的情况
stream.destroy(); // 调用流的 destroy 方法
// node.js >= 12, ^11.1.0, ^10.15.1
stream.destroy()
} else if (stream._destroy && typeof stream.close === 'function') {
// 处理 Node.js 版本为 7 或 8 的情况
stream.destroyed = true; // 标记流为已销毁
stream.close(); // 调用流的 close 方法
// node.js 7, 8
stream.destroyed = true
stream.close()
} else {
// 备用方案
// fallback
// istanbul ignore next
stream.destroy(); // 调用流的 destroy 方法
stream.destroy()
}
} else if (typeof stream.close === 'function') {
// 处理 Node.js 版本小于 8 的备用方案
closeZlibStream(stream); // 调用关闭 zlib 流的函数
// node.js < 8 fallback
closeZlibStream(stream)
}
}
/**
* 确定流是否具有 destroy 方法
* Determine if stream has destroy.
* @private
*/
function hasDestroy(stream) {
return stream instanceof Stream && // 检查流是否是 Stream 的实例
typeof stream.destroy === 'function'; // 检查流是否具有 destroy 方法
function hasDestroy (stream) {
return stream instanceof Stream &&
typeof stream.destroy === 'function'
}
/**
* 确定 val 是否是 EventEmitter
* Determine if val is EventEmitter.
* @private
*/
function isEventEmitter(val) {
return val instanceof EventEmitter; // 检查 val 是否是 EventEmitter 的实例
function isEventEmitter (val) {
return val instanceof EventEmitter
}
/**
* 确定流是否是 fs.ReadStream
* Determine if stream is fs.ReadStream stream.
* @private
*/
function isFsReadStream(stream) {
return stream instanceof ReadStream; // 检查流是否是 ReadStream 的实例
function isFsReadStream (stream) {
return stream instanceof ReadStream
}
/**
* 确定流是否是 Zlib
* Determine if stream is Zlib stream.
* @private
*/
function isZlibStream(stream) {
// 检查流是否是 Zlib 的不同类型的实例
function isZlibStream (stream) {
return stream instanceof Zlib.Gzip ||
stream instanceof Zlib.Gunzip ||
stream instanceof Zlib.Deflate ||
stream instanceof Zlib.DeflateRaw ||
stream instanceof Zlib.Inflate ||
stream instanceof Zlib.InflateRaw ||
stream instanceof Zlib.Unzip;
stream instanceof Zlib.Gunzip ||
stream instanceof Zlib.Deflate ||
stream instanceof Zlib.DeflateRaw ||
stream instanceof Zlib.Inflate ||
stream instanceof Zlib.InflateRaw ||
stream instanceof Zlib.Unzip
}
/**
@ -199,86 +184,26 @@ function isZlibStream(stream) {
* @private
*/
/**
* 销毁 Zlib
*
* @param {object} stream - 要销毁的 Zlib 流对象
*/
function destroyZlibStream(stream) {
// 检查流是否具有 destroy 方法
if (typeof stream.destroy === 'function') {
// node.js 核心 bug 的解决方法
// istanbul ignore if: node.js 0.8
if (stream._binding) {
// 处理 Node.js 版本小于 0.10.0 的情况
stream.destroy(); // 调用流的 destroy 方法
// 如果流正在处理数据
if (stream._processing) {
stream._needDrain = true; // 标记需要排空
stream.once('drain', onDrainClearBinding); // 一旦排空,调用 onDrainClearBinding 函数
} else {
stream._binding.clear(); // 清除绑定
}
} else if (stream._destroy && stream._destroy !== Stream.Transform.prototype._destroy) {
// 处理 Node.js 版本大于等于 12或 11.1.0,或 10.15.1 的情况
stream.destroy(); // 调用流的 destroy 方法
} else if (stream._destroy && typeof stream.close === 'function') {
// 处理 Node.js 版本为 7 或 8 的情况
stream.destroyed = true; // 标记流为已销毁
stream.close(); // 调用流的 close 方法
} else {
// 备用方案
// istanbul ignore next
stream.destroy(); // 调用流的 destroy 方法
}
} else if (typeof stream.close === 'function') {
// 处理 Node.js 版本小于 8 的备用方案
closeZlibStream(stream); // 调用关闭 zlib 流的函数
}
}
/**
* 确定流是否具有 destroy 方法
* @private
*/
function hasDestroy(stream) {
return stream instanceof Stream && // 检查流是否是 Stream 的实例
typeof stream.destroy === 'function'; // 检查流是否具有 destroy 方法
}
/**
* 确定 val 是否是 EventEmitter
* @private
*/
function isEventEmitter(val) {
return val instanceof EventEmitter; // 检查 val 是否是 EventEmitter 的实例
}
function noop () {}
/**
* 确定流是否是 fs.ReadStream
* On drain handler to clear binding.
* @private
*/
function isFsReadStream(stream) {
return stream instanceof ReadStream; // 检查流是否是 ReadStream 的实例
// istanbul ignore next: node.js 0.8
function onDrainClearBinding () {
this._binding.clear()
}
/**
* 确定流是否是 Zlib
* On open handler to close stream.
* @private
*/
function isZlibStream(stream) {
// 检查流是否是 Zlib 的不同类型的实例
return stream instanceof Zlib.Gzip ||
stream instanceof Zlib.Gunzip ||
stream instanceof Zlib.Deflate ||
stream instanceof Zlib.DeflateRaw ||
stream instanceof Zlib.Inflate ||
stream instanceof Zlib.InflateRaw ||
stream instanceof Zlib.Unzip;
function onOpenClose () {
if (typeof this.fd === 'number') {
// actually close down the fd
this.close()
}
}

39
node_modules/dom-walk/index.js generated vendored

@ -1,29 +1,24 @@
/**
* 空函数通常用于占位或作为默认回调
* @private
*/
var slice = Array.prototype.slice
function noop() {}
module.exports = iterativelyWalk
/**
* 排空处理程序用于清除绑定
* @private
*/
function iterativelyWalk(nodes, cb) {
if (!('length' in nodes)) {
nodes = [nodes]
}
nodes = slice.call(nodes)
// istanbul ignore next: node.js 0.8
function onDrainClearBinding() {
this._binding.clear(); // 调用绑定的清除方法
}
while(nodes.length) {
var node = nodes.shift(),
ret = cb(node)
/**
* 打开处理程序用于关闭流
* @private
*/
if (ret) {
return ret
}
function onOpenClose() {
// 检查文件描述符 (fd) 是否为数字
if (typeof this.fd === 'number') {
// 实际关闭文件描述符
this.close(); // 调用流的 close 方法
if (node.childNodes && node.childNodes.length) {
nodes = slice.call(node.childNodes).concat(nodes)
}
}
}

95
node_modules/ecc-jsbn/index.js generated vendored

@ -1,61 +1,58 @@
// 引入所需的模块
var crypto = require("crypto"); // 用于加密和随机数生成
var BigInteger = require("jsbn").BigInteger; // 大整数库
var ECPointFp = require("./lib/ec.js").ECPointFp; // 引入椭圆曲线点类
var Buffer = require("safer-buffer").Buffer; // 安全的 Buffer 实现
exports.ECCurves = require("./lib/sec.js"); // 导出椭圆曲线库
var crypto = require("crypto");
var BigInteger = require("jsbn").BigInteger;
var ECPointFp = require("./lib/ec.js").ECPointFp;
var Buffer = require("safer-buffer").Buffer;
exports.ECCurves = require("./lib/sec.js");
// 零填充函数
function unstupid(hex, len) {
// 如果 hex 长度大于或等于 len返回 hex否则递归调用自身前面加一个 "0"
return (hex.length >= len) ? hex : unstupid("0" + hex, len);
// zero prepad
function unstupid(hex,len)
{
return (hex.length >= len) ? hex : unstupid("0"+hex,len);
}
// 导出 ECKey 构造函数
exports.ECKey = function(curve, key, isPublic) {
var priv; // 私钥
var c = curve(); // 获取曲线
var n = c.getN(); // 获取曲线的阶
var bytes = Math.floor(n.bitLength() / 8); // 计算字节数
exports.ECKey = function(curve, key, isPublic)
{
var priv;
var c = curve();
var n = c.getN();
var bytes = Math.floor(n.bitLength()/8);
// 如果提供了密钥
if (key) {
if (isPublic) {
if(key)
{
if(isPublic)
{
var curve = c.getCurve();
// 解码公钥
// var x = key.slice(1,bytes+1); // skip the 04 for uncompressed format
// var y = key.slice(bytes+1);
// this.P = new ECPointFp(curve,
// curve.fromBigInteger(new BigInteger(x.toString("hex"), 16)),
// curve.fromBigInteger(new BigInteger(y.toString("hex"), 16)));
this.P = curve.decodePointHex(key.toString("hex"));
} else {
// 如果密钥长度不等于字节数,则返回 false
if (key.length != bytes) return false;
// 将密钥转换为大整数
priv = new BigInteger(key.toString("hex"), 16);
}else{
if(key.length != bytes) return false;
priv = new BigInteger(key.toString("hex"), 16);
}
} else {
// 生成随机私钥
var n1 = n.subtract(BigInteger.ONE); // n - 1
var r = new BigInteger(crypto.randomBytes(n.bitLength())); // 生成随机数
priv = r.mod(n1).add(BigInteger.ONE); // 确保私钥在 [1, n-1] 范围内
this.P = c.getG().multiply(priv); // 计算公钥 P = G * priv
}else{
var n1 = n.subtract(BigInteger.ONE);
var r = new BigInteger(crypto.randomBytes(n.bitLength()));
priv = r.mod(n1).add(BigInteger.ONE);
this.P = c.getG().multiply(priv);
}
// 如果公钥 P 存在
if (this.P) {
// 将公钥编码为压缩格式并转换为 Buffer
this.PublicKey = Buffer.from(c.getCurve().encodeCompressedPointHex(this.P), "hex");
if(this.P)
{
// var pubhex = unstupid(this.P.getX().toBigInteger().toString(16),bytes*2)+unstupid(this.P.getY().toBigInteger().toString(16),bytes*2);
// this.PublicKey = Buffer.from("04"+pubhex,"hex");
this.PublicKey = Buffer.from(c.getCurve().encodeCompressedPointHex(this.P),"hex");
}
// 如果私钥存在
if (priv) {
// 将私钥转换为 Buffer
this.PrivateKey = Buffer.from(unstupid(priv.toString(16), bytes * 2), "hex");
// 定义派生共享密钥的方法
this.deriveSharedSecret = function(key) {
// 检查传入的密钥是否有效
if (!key || !key.P) return false;
// 计算共享密钥 S = key.P * priv
if(priv)
{
this.PrivateKey = Buffer.from(unstupid(priv.toString(16),bytes*2),"hex");
this.deriveSharedSecret = function(key)
{
if(!key || !key.P) return false;
var S = key.P.multiply(priv);
// 返回共享密钥的 X 坐标,转换为 Buffer
return Buffer.from(unstupid(S.getX().toBigInteger().toString(16), bytes * 2), "hex");
}
return Buffer.from(unstupid(S.getX().toBigInteger().toString(16),bytes*2),"hex");
}
}
}

78
node_modules/ee-first/index.js generated vendored

@ -4,94 +4,92 @@
* MIT Licensed
*/
'use strict'; // 使用严格模式
'use strict'
/**
* 模块导出
* Module exports.
* @public
*/
module.exports = first;
module.exports = first
/**
* 获取一组事件发射器和事件对中的第一个事件
* Get the first event in a set of event emitters and event pairs.
*
* @param {array} stuff - 包含事件发射器和事件的数组
* @param {function} done - 当事件触发时调用的回调函数
* @param {array} stuff
* @param {function} done
* @public
*/
function first(stuff, done) {
// 检查 stuff 是否为数组
if (!Array.isArray(stuff))
throw new TypeError('arg must be an array of [ee, events...] arrays');
throw new TypeError('arg must be an array of [ee, events...] arrays')
var cleanups = []; // 用于存储清理函数的数组
var cleanups = []
// 遍历传入的 stuff 数组
for (var i = 0; i < stuff.length; i++) {
var arr = stuff[i];
var arr = stuff[i]
// 检查每个元素是否为数组,且长度至少为 2
if (!Array.isArray(arr) || arr.length < 2)
throw new TypeError('each array member must be [ee, events...]');
throw new TypeError('each array member must be [ee, events...]')
var ee = arr[0]; // 获取事件发射器
var ee = arr[0]
// 遍历事件数组
for (var j = 1; j < arr.length; j++) {
var event = arr[j];
var fn = listener(event, callback); // 创建事件监听器
var event = arr[j]
var fn = listener(event, callback)
// 监听事件
ee.on(event, fn);
// 将此监听器推入清理列表
// listen to the event
ee.on(event, fn)
// push this listener to the list of cleanups
cleanups.push({
ee: ee,
event: event,
fn: fn,
});
})
}
}
// 事件触发时的回调函数
function callback() {
cleanup(); // 清理所有监听器
done.apply(null, arguments); // 调用传入的 done 函数
cleanup()
done.apply(null, arguments)
}
// 清理函数,移除所有监听器
function cleanup() {
var x;
var x
for (var i = 0; i < cleanups.length; i++) {
x = cleanups[i];
x.ee.removeListener(x.event, x.fn); // 移除监听器
x = cleanups[i]
x.ee.removeListener(x.event, x.fn)
}
}
// 返回一个 thunk 函数
function thunk(fn) {
done = fn; // 更新 done 函数
done = fn
}
thunk.cancel = cleanup; // 将清理函数附加到 thunk 上
thunk.cancel = cleanup
return thunk; // 返回 thunk 函数
return thunk
}
/**
* 创建事件监听器
* Create the event listener.
* @private
*/
function listener(event, done) {
return function onevent(arg1) {
var args = new Array(arguments.length); // 创建一个新数组来存储参数
var ee = this; // 获取事件发射器的上下文
var err = event === 'error' ? arg1 : null; // 如果是 'error' 事件,设置错误参数
var args = new Array(arguments.length)
var ee = this
var err = event === 'error'
? arg1
: null
// 复制参数以防止 arguments 逃逸作用域
// copy args to prevent arguments escaping scope
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
args[i] = arguments[i]
}
done(err, ee, event, args); // 调用 done 函数,传入错误、事件发射器、事件和参数
};
done(err, ee, event, args)
}
}

@ -1,52 +1,36 @@
'use strict';
// 引入 `bn.js` 库,用于处理大整数相关操作
var BN = require('bn.js');
// 引入自定义的 `utils` 模块,其中包含了一些工具函数
var utils = require('../utils');
// 从 `utils` 模块中获取 `getNAF` 函数用于将整数转换为某种非相邻形式Non-Adjacent FormNAF表示
var getNAF = utils.getNAF;
// 从 `utils` 模块中获取 `getJSF` 函数,其功能可能与特定的数学表示形式转换相关(从函数名推测可能是联合稀疏形式相关操作)
var getJSF = utils.getJSF;
// 从 `utils` 模块中获取 `assert` 函数,通常用于进行条件判断并在不满足条件时抛出异常
var assert = utils.assert;
// `BaseCurve` 构造函数,用于创建曲线相关的基础对象,不同类型的曲线可能继承自这个基础类来扩展各自的特性
function BaseCurve(type, conf) {
// 记录曲线的类型
this.type = type;
// 将配置中以十六进制字符串表示的素数 `p` 转换为 `BN` 类型的大整数,并存储起来,这个 `p` 可能与曲线所在的有限域相关
this.p = new BN(conf.p, 16);
// 判断配置中是否有 `prime` 属性,如果有则使用 `BN.red` 方法进行某种与素数相关的初始化可能是蒙哥马利Montgomery相关设置
// 如果没有则针对当前曲线的素数 `p` 使用 `BN.mont` 方法进行蒙哥马利初始化,`red` 可能用于后续的快速模运算等操作
this.red = conf.prime? BN.red(conf.prime) : BN.mont(this.p);
// Use Montgomery, when there is no fast reduction for the prime
this.red = conf.prime ? BN.red(conf.prime) : BN.mont(this.p);
// 创建表示数字 0 的蒙哥马利形式,存储在 `this.zero` 中,方便后续在蒙哥马利域下进行运算时使用
// Useful for many curves
this.zero = new BN(0).toRed(this.red);
// 创建表示数字 1 的蒙哥马利形式,存储在 `this.one` 中,同样用于蒙哥马利域运算
this.one = new BN(1).toRed(this.red);
// 创建表示数字 2 的蒙哥马利形式,存储在 `this.two` 中
this.two = new BN(2).toRed(this.red);
// 如果配置中有 `n` 属性(可能表示曲线的阶数),则将其转换为 `BN` 类型的大整数并存储,否则 `this.n` 为 `undefined`
// Curve configuration, optional
this.n = conf.n && new BN(conf.n, 16);
// 如果配置中有 `g` 属性(可能表示曲线的基点,生成元),则调用 `pointFromJSON` 方法(此方法需在子类中实现,这里是抽象的调用)将其转换为相应的点对象并存储,
// 同时根据 `gRed` 属性进行相关设置(具体取决于 `pointFromJSON` 方法的实现),如果没有 `g` 属性则 `this.g` 为 `undefined`
this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed);
// 创建一些临时数组,用于后续计算过程中的临时数据存储,数组长度都初始化为 4具体用途会在相关计算方法中体现
// Temporary arrays
this._wnafT1 = new Array(4);
this._wnafT2 = new Array(4);
this._wnafT3 = new Array(4);
this._wnafT4 = new Array(4);
// 如果 `this.n` 存在(即曲线阶数已定义),则获取其位数并存储在 `this._bitLength` 中,否则 `this._bitLength` 为 0
this._bitLength = this.n? this.n.bitLength() : 0;
this._bitLength = this.n ? this.n.bitLength() : 0;
// 进行广义的 Greg Maxwell's trick 相关操作(一种优化技巧,可能与曲线运算性能相关)
// 计算 `this.p` 除以 `this.n` 的结果,如果不存在 `this.n` 或者除法结果大于 100则将 `this.redN` 设置为 `null`
// 否则设置 `this._maxwellTrick` 为 `true`,并将 `this.n` 转换为蒙哥马利形式存储在 `this.redN` 中
// Generalized Greg Maxwell's trick
var adjustCount = this.n && this.p.div(this.n);
if (!adjustCount || adjustCount.cmpn(100) > 0) {
this.redN = null;
@ -55,35 +39,25 @@ function BaseCurve(type, conf) {
this.redN = this.n.toRed(this.red);
}
}
// 将 `BaseCurve` 函数暴露出去,方便其他模块使用这个基础曲线类
module.exports = BaseCurve;
// 在 `BaseCurve` 的原型上定义 `point` 方法,此方法在 `BaseCurve` 类中是抽象的,需要在子类中具体实现,
// 这里直接抛出一个错误表示未实现,调用者应该在子类中重写这个方法来返回相应的曲线点对象
BaseCurve.prototype.point = function point() {
throw new Error('Not implemented');
};
// 在 `BaseCurve` 的原型上定义 `validate` 方法,同样是抽象方法,用于验证曲线相关的一些条件是否满足,
// 在子类中需要重写这个方法来实现具体的验证逻辑,这里直接抛出错误表示未实现
BaseCurve.prototype.validate = function validate() {
throw new Error('Not implemented');
};
// `_fixedNafMul` 方法用于基于固定宽度的非相邻形式NAF进行乘法相关运算可能是点乘之类的操作具体取决于上下文和 `p`、`k` 的含义)
BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) {
// 断言 `p` 对象有 `precomputed` 属性(可能表示 `p` 相关的预计算数据已准备好,具体取决于对象结构和应用场景),如果不满足则抛出异常
assert(p.precomputed);
// 获取 `p` 对象的 `_getDoubles` 方法返回的结果,这个方法应该返回与双倍相关的数据(比如预计算的双倍点等信息,具体取决于实现)
var doubles = p._getDoubles();
// 使用 `getNAF` 函数将整数 `k` 转换为宽度为 1 的非相邻形式表示,传入曲线的位数 `this._bitLength` 用于控制相关计算范围等
var naf = getNAF(k, 1, this._bitLength);
// 计算一个与双倍相关的参数 `I`,具体计算逻辑根据给定的表达式,可能与双倍点的步长等信息有关,用于后续的计算转换
var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0? 2 : 1);
var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1);
I /= 3;
// 将 `naf` 表示转换为更适合窗口形式的表示形式,存储在 `repr` 数组中,具体转换逻辑通过循环遍历 `naf` 并按一定规则合并位来实现
// Translate into more windowed form
var repr = [];
var j;
var nafW;
@ -94,111 +68,89 @@ BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) {
repr.push(nafW);
}
// 创建两个曲线点对象 `a` 和 `b`,初始化为 `null`(这里的 `jpoint` 方法应该是创建特定类型曲线点的方法,具体功能取决于实现)
var a = this.jpoint(null, null, null);
var b = this.jpoint(null, null, null);
// 外层循环,根据 `I` 的值进行迭代,可能与计算次数等相关
for (var i = I; i > 0; i--) {
// 内层循环,遍历 `repr` 数组中的每个元素(即窗口化后的 `naf` 表示形式)
for (j = 0; j < repr.length; j++) {
nafW = repr[j];
// 如果当前元素等于 `i`,则调用 `b` 的 `mixedAdd` 方法(可能是混合加法操作,具体取决于曲线点对象的实现)将对应的双倍点加到 `b` 上
if (nafW === i)
b = b.mixedAdd(doubles.points[j]);
// 如果当前元素等于 `-i`,则调用 `b` 的 `mixedAdd` 方法将对应的双倍点的相反数加到 `b` 上
else if (nafW === -i)
b = b.mixedAdd(doubles.points[j].neg());
}
// 将 `b` 的结果累加到 `a` 上,通过调用 `a` 的 `add` 方法(点加法操作)
a = a.add(b);
}
// 根据 `p` 的类型,如果是 `affine` 类型(仿射坐标类型,一种表示曲线点的方式),则调用 `toP` 方法(可能是转换为某种特定格式的点表示)将 `a` 转换后返回,否则直接返回 `a`
return a.toP();
};
// `_wnafMul` 方法,用于基于窗口宽度为 4 的非相邻形式w-NAF进行乘法相关运算同样可能是点乘操作等
BaseCurve.prototype._wnafMul = function _wnafMul(p, k) {
// 设置窗口宽度为 4这个宽度会影响后续非相邻形式的计算和相关预计算等操作
var w = 4;
// 调用 `p` 对象的 `_getNAFPoints` 方法(应该是获取与非相邻形式相关的预计算点信息),传入窗口宽度 `w`,获取预计算的点相关信息并存储在 `nafPoints` 中,
// 同时更新 `w` 为实际获取到的窗口宽度(可能在 `_getNAFPoints` 方法中根据实际情况进行了调整),获取预计算的点数组存储在 `wnd` 中
// Precompute window
var nafPoints = p._getNAFPoints(w);
w = nafPoints.wnd;
var wnd = nafPoints.points;
// 使用 `getNAF` 函数将整数 `k` 转换为宽度为 `w` 的非相邻形式表示,传入曲线的位数 `this._bitLength` 用于控制相关计算范围等
// Get NAF form
var naf = getNAF(k, w, this._bitLength);
// 创建一个曲线点对象 `acc`,初始化为 `null`(同样通过 `jpoint` 方法创建,具体功能取决于实现),用于累加计算结果
// Add `this`*(N+1) for every w-NAF index
var acc = this.jpoint(null, null, null);
// 从 `naf` 数组的末尾开始向前遍历(反向遍历,可能与计算顺序相关)
for (var i = naf.length - 1; i >= 0; i--) {
// 统计连续的 0 的个数,通过循环递减 `i` 直到遇到非 0 值或者 `i` 小于 0
// Count zeroes
for (var l = 0; i >= 0 && naf[i] === 0; i--)
l++;
if (i >= 0)
l++;
// 根据连续 0 的个数对 `acc` 进行双倍操作(`dblp` 方法可能是点的双倍运算,具体取决于曲线点对象的实现)
acc = acc.dblp(l);
if (i < 0)
break;
var z = naf[i];
// 断言 `z` 不为 0如果为 0 则抛出异常(这里可能是基于算法逻辑要求非相邻形式表示中不应出现连续多个 0 的情况等)
assert(z!== 0);
assert(z !== 0);
if (p.type === 'affine') {
// 如果 `p` 的类型是 `affine`(仿射坐标类型),根据 `z` 的正负情况,调用 `acc` 的 `mixedAdd` 方法将对应的预计算点或其相反数加到 `acc` 上(`J +- P` 表示的可能是某种点加法逻辑)
// J +- P
if (z > 0)
acc = acc.mixedAdd(wnd[(z - 1) >> 1]);
else
acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg());
} else {
// 如果 `p` 不是 `affine` 类型,同样根据 `z` 的正负情况,调用 `acc` 的 `add` 方法将对应的预计算点或其相反数加到 `acc` 上(`J +- J` 表示的可能是另一种点加法逻辑)
// J +- J
if (z > 0)
acc = acc.add(wnd[(z - 1) >> 1]);
else
acc = acc.add(wnd[(-z - 1) >> 1].neg());
}
}
// 根据 `p` 的类型,如果是 `affine` 类型则调用 `toP` 方法将 `acc` 转换后返回,否则直接返回 `acc`
return p.type === 'affine'? acc.toP() : acc;
return p.type === 'affine' ? acc.toP() : acc;
};
// `_wnafMulAdd` 方法用于基于窗口非相邻形式w-NAF进行乘法和加法相关的混合运算传入了多个参数用于控制计算过程
BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW,
points,
coeffs,
len,
jacobianResult) {
// 获取临时数组 `this._wnafT1`,用于存储窗口宽度相关信息(从变量名推测),可能在后续计算中用于记录每个点对应的窗口宽度等
points,
coeffs,
len,
jacobianResult) {
var wndWidth = this._wnafT1;
// 获取临时数组 `this._wnafT2`,用于存储预计算的点相关信息(同样从变量名推测),可能是每个点对应的窗口非相邻形式的预计算点数组等
var wnd = this._wnafT2;
// 获取临时数组 `this._wnafT3`用于存储非相邻形式NAF表示的数据推测可能是每个系数对应的非相邻形式表示等
var naf = this._wnafT3;
// 初始化一个变量 `max` 为 0用于记录后续计算中出现的最大长度可能是 NAF 表示的长度等)
// Fill all arrays
var max = 0;
var i;
var j;
var p;
// 循环遍历传入的点数组 `points`,填充相关的临时数组 `wndWidth` 和 `wnd`
for (i = 0; i < len; i++) {
p = points[i];
// 调用每个点 `p` 的 `_getNAFPoints` 方法(获取与非相邻形式相关的预计算点信息),传入默认窗口宽度 `defW`,获取并存储窗口宽度和预计算点信息到相应的临时数组中
var nafPoints = p._getNAFPoints(defW);
wndWidth[i] = nafPoints.wnd;
wnd[i] = nafPoints.points;
}
// 从后往前每隔 2 个元素进行循环可能是对相邻的点和系数进行组合操作具体取决于算法逻辑用于组合小窗口的非相邻形式NAF表示
// Comb small window NAFs
for (i = len - 1; i >= 1; i -= 2) {
var a = i - 1;
var b = i;
// 如果两个相邻点对应的窗口宽度不为 1则分别调用 `getNAF` 函数将对应的系数转换为非相邻形式表示,
// 并更新 `max` 为当前两个非相邻形式表示长度中的最大值,然后继续下一轮循环
if (wndWidth[a]!== 1 || wndWidth[b]!== 1) {
if (wndWidth[a] !== 1 || wndWidth[b] !== 1) {
naf[a] = getNAF(coeffs[a], wndWidth[a], this._bitLength);
naf[b] = getNAF(coeffs[b], wndWidth[b], this._bitLength);
max = Math.max(naf[a].length, max);
@ -206,8 +158,6 @@ BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW,
continue;
}
// 创建一个包含 4 个元素的数组 `comb`,用于存储一些点的组合情况,初始值部分设置为 `points[a]` 和 `null`
// 具体含义和后续用途取决于下面的条件判断和赋值逻辑,这里可能是在尝试通过点的组合来优化计算等
var comb = [
points[a], /* 1 */
null, /* 3 */
@ -215,9 +165,18 @@ BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW,
points[b], /* 7 */
];
// 尝试避免使用射影坐标Projective points一种曲线点的表示方式根据两个点的 `y` 坐标的比较情况进行不同的点组合操作,
// 如果两个点的 `y` 坐标相等,则进行特定的点加法操作并赋值给 `comb` 数组中的相应位置,
// 如果两个点的 `y` 坐标互为相反数(通过 `redNeg` 方法判断,可能是蒙哥马利域下的取相反数操作),则进行另
// Try to avoid Projective points, if possible
if (points[a].y.cmp(points[b].y) === 0) {
comb[1] = points[a].add(points[b]);
comb[2] = points[a].toJ().mixedAdd(points[b].neg());
} else if (points[a].y.cmp(points[b].y.redNeg()) === 0) {
comb[1] = points[a].toJ().mixedAdd(points[b]);
comb[2] = points[a].add(points[b].neg());
} else {
comb[1] = points[a].toJ().mixedAdd(points[b]);
comb[2] = points[a].toJ().mixedAdd(points[b].neg());
}
var index = [
-3, /* -1 -1 */
-1, /* -1 0 */
@ -230,229 +189,193 @@ BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW,
3, /* 1 1 */
];
// 从给定的系数中获取 JSFJacobian Scalar Form
var jsf = getJSF(coeffs[a], coeffs[b]); // 获取系数 a 和 b 的 Jacobian 标量形式
max = Math.max(jsf[0].length, max); // 更新最大长度
naf[a] = new Array(max); // 为 naf 数组的 a 分配最大长度
naf[b] = new Array(max); // 为 naf 数组的 b 分配最大长度
// 遍历最大长度
var jsf = getJSF(coeffs[a], coeffs[b]);
max = Math.max(jsf[0].length, max);
naf[a] = new Array(max);
naf[b] = new Array(max);
for (j = 0; j < max; j++) {
var ja = jsf[0][j] | 0; // 获取 jsf 的第一个部分的值并转为整数
var jb = jsf[1][j] | 0; // 获取 jsf 的第二个部分的值并转为整数
var ja = jsf[0][j] | 0;
var jb = jsf[1][j] | 0;
// 根据 ja 和 jb 的值更新 naf 数组
naf[a][j] = index[(ja + 1) * 3 + (jb + 1)]; // 使用 index 数组计算 naf[a][j]
naf[b][j] = 0; // 将 naf[b][j] 设置为 0
wnd[a] = comb; // 将 wnd[a] 设置为 comb组合预计算值
naf[a][j] = index[(ja + 1) * 3 + (jb + 1)];
naf[b][j] = 0;
wnd[a] = comb;
}
}
// 初始化一个新的 Jacobian 点
var acc = this.jpoint(null, null, null); // 创建一个新的 Jacobian 点
var tmp = this._wnafT4; // 获取临时数组,用于存储 WNAF宽度非负数表示法
// 从最大长度开始逆向迭代
for (i = max; i >= 0; i--) {
var k = 0; // 初始化 k用于记录零的数量
// 检查当前索引 i 的 WNAF 值
while (i >= 0) {
var zero = true; // 假设当前为零
for (j = 0; j < len; j++) {
tmp[j] = naf[j][i] | 0; // 将 naf[j][i] 的值存入临时数组并转为整数
if (tmp[j] !== 0) // 如果值不为零
zero = false; // 标记为非零
}
if (!zero) // 如果有非零值
break; // 退出循环
k++; // 计数零的数量
i--; // 减少 i 值
}
if (i >= 0) // 如果 i 仍然有效
k++; // 增加 k
acc = acc.dblp(k); // 对累加器进行 k 次双倍操作
if (i < 0) // 如果 i 小于 0退出循环
break;
var acc = this.jpoint(null, null, null);
var tmp = this._wnafT4;
for (i = max; i >= 0; i--) {
var k = 0;
// 遍历所有的 len
while (i >= 0) {
var zero = true;
for (j = 0; j < len; j++) {
var z = tmp[j]; // 获取临时数组的当前值
p; // 声明变量 p
if (z === 0) // 如果 z 为零
continue; // 跳过
else if (z > 0) // 如果 z 为正
p = wnd[j][(z - 1) >> 1]; // 获取正值的预计算点
else if (z < 0) // 如果 z 为负
p = wnd[j][(-z - 1) >> 1].neg(); // 获取负值的预计算点并取反
// 根据 p 的类型选择不同的加法方式
if (p.type === 'affine') // 如果 p 是仿射点
acc = acc.mixedAdd(p); // 使用混合加法
else // 如果 p 是 Jacobian 点
acc = acc.add(p); // 使用普通加法
tmp[j] = naf[j][i] | 0;
if (tmp[j] !== 0)
zero = false;
}
if (!zero)
break;
k++;
i--;
}
if (i >= 0)
k++;
acc = acc.dblp(k);
if (i < 0)
break;
// 清理引用,将 wnd 数组的每个元素设置为 null
for (i = 0; i < len; i++)
wnd[i] = null; // 清除 wnd 数组中的引用
for (j = 0; j < len; j++) {
var z = tmp[j];
p;
if (z === 0)
continue;
else if (z > 0)
p = wnd[j][(z - 1) >> 1];
else if (z < 0)
p = wnd[j][(-z - 1) >> 1].neg();
if (p.type === 'affine')
acc = acc.mixedAdd(p);
else
acc = acc.add(p);
}
}
// Zeroify references
for (i = 0; i < len; i++)
wnd[i] = null;
if (jacobianResult)
return acc;
else
return acc.toP();
};
// 根据 jacobianResult 的值决定返回值
if (jacobianResult)
return acc; // 如果需要 Jacobian 结果,则返回 acc
else
return acc.toP(); // 否则返回转换为仿射坐标的 acc
};
function BasePoint(curve, type) {
this.curve = curve;
this.type = type;
this.precomputed = null;
}
BaseCurve.BasePoint = BasePoint;
// 定义 BasePoint 构造函数
function BasePoint(curve, type) {
this.curve = curve; // 保存曲线对象
this.type = type; // 保存类型
this.precomputed = null; // 初始化预计算值为 null
}
BasePoint.prototype.eq = function eq(/*other*/) {
throw new Error('Not implemented');
};
// 将 BasePoint 赋值给 BaseCurve 的 BasePoint 属性
BaseCurve.BasePoint = BasePoint;
BasePoint.prototype.validate = function validate() {
return this.curve.validate(this);
};
// 定义点的相等性比较方法,当前未实现
BasePoint.prototype.eq = function eq(/*other*/) {
throw new Error('Not implemented'); // 抛出未实现的错误
};
BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) {
bytes = utils.toArray(bytes, enc);
// 验证当前点是否在曲线上
BasePoint.prototype.validate = function validate() {
return this.curve.validate(this); // 调用曲线的验证方法
};
var len = this.p.byteLength();
// 解码点的字节表示
BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) {
// 将字节转换为数组
bytes = utils.toArray(bytes, enc);
var len = this.p.byteLength(); // 获取曲线的参数长度
// 处理未压缩、混合奇偶形式的点
if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) &&
bytes.length - 1 === 2 * len) {
if (bytes[0] === 0x06)
assert(bytes[bytes.length - 1] % 2 === 0); // 校验最后一个字节为偶数
else if (bytes[0] === 0x07)
assert(bytes[bytes.length - 1] % 2 === 1); // 校验最后一个字节为奇数
// 创建点对象并返回
var res = this.point(bytes.slice(1, 1 + len),
bytes.slice(1 + len, 1 + 2 * len));
return res; // 返回解码后的点
} else if ((bytes[0] === 0x02 || bytes[0] === 0x03) &&
bytes.length - 1 === len) {
// 处理压缩形式的点
return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03);
}
throw new Error('Unknown point format'); // 抛出未知格式的错误
};
// uncompressed, hybrid-odd, hybrid-even
if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) &&
bytes.length - 1 === 2 * len) {
if (bytes[0] === 0x06)
assert(bytes[bytes.length - 1] % 2 === 0);
else if (bytes[0] === 0x07)
assert(bytes[bytes.length - 1] % 2 === 1);
// 压缩编码点
BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) {
return this.encode(enc, true); // 调用 encode 方法,设置为压缩格式
};
var res = this.point(bytes.slice(1, 1 + len),
bytes.slice(1 + len, 1 + 2 * len));
// 编码点为字节数组
BasePoint.prototype._encode = function _encode(compact) {
var len = this.curve.p.byteLength(); // 获取曲线参数长度
var x = this.getX().toArray('be', len); // 获取 x 坐标并转换为字节数组
return res;
} else if ((bytes[0] === 0x02 || bytes[0] === 0x03) &&
bytes.length - 1 === len) {
return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03);
}
throw new Error('Unknown point format');
};
if (compact)
return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x); // 返回压缩格式
BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) {
return this.encode(enc, true);
};
return [ 0x04 ].concat(x, this.getY().toArray('be', len)); // 返回未压缩格式
};
BasePoint.prototype._encode = function _encode(compact) {
var len = this.curve.p.byteLength();
var x = this.getX().toArray('be', len);
// 编码点为指定格式
BasePoint.prototype.encode = function encode(enc, compact) {
return utils.encode(this._encode(compact), enc); // 调用 utils.encode 进行编码
};
if (compact)
return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x);
// 预计算点的倍数
BasePoint.prototype.precompute = function precompute(power) {
if (this.precomputed)
return this; // 如果已经预计算,直接返回
var precomputed = {
doubles: null, // 存储倍点
naf: null, // 存储 NAF 点
beta: null, // 存储 beta
};
precomputed.naf = this._getNAFPoints(8); // 获取 NAF 点
precomputed.doubles = this._getDoubles(4, power); // 获取倍点
precomputed.beta = this._getBeta(); // 获取 beta
this.precomputed = precomputed; // 存储预计算结果
return this; // 返回当前点
};
return [ 0x04 ].concat(x, this.getY().toArray('be', len));
};
// 检查是否有足够的倍点
BasePoint.prototype._hasDoubles = function _hasDoubles(k) {
if (!this.precomputed)
return false; // 如果没有预计算,返回 false
BasePoint.prototype.encode = function encode(enc, compact) {
return utils.encode(this._encode(compact), enc);
};
var doubles = this.precomputed.doubles; // 获取倍点
if (!doubles)
return false; // 如果没有倍点,返回 false
BasePoint.prototype.precompute = function precompute(power) {
if (this.precomputed)
return this;
// 检查倍点数量是否足够
return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step);
var precomputed = {
doubles: null,
naf: null,
beta: null,
};
precomputed.naf = this._getNAFPoints(8);
precomputed.doubles = this._getDoubles(4, power);
precomputed.beta = this._getBeta();
this.precomputed = precomputed;
// 获取倍点的函数
BasePoint.prototype._getDoubles = function _getDoubles(step, power) {
if (this.precomputed && this.precomputed.doubles)
return this.precomputed.doubles; // 如果已经预计算,直接返回
var doubles = [ this ]; // 初始化倍点数组
var acc = this; // 记录当前点
for (var i = 0; i < power; i += step) {
for (var j = 0; j < step; j++)
acc = acc.dbl(); // 计算倍点
doubles.push(acc); // 添加倍点到数组
}
return {
step: step,
points: doubles, // 返回倍点和步长
};
};
return this;
};
BasePoint.prototype._hasDoubles = function _hasDoubles(k) {
if (!this.precomputed)
return false;
// 获取 NAF 点的函数
BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) {
// 如果已经预计算过 NAF 点,直接返回
if (this.precomputed && this.precomputed.naf)
return this.precomputed.naf;
var doubles = this.precomputed.doubles;
if (!doubles)
return false;
var res = [ this ]; // 初始化结果数组,包含当前点
var max = (1 << wnd) - 1; // 计算最大值wnd 为窗口大小
var dbl = max === 1 ? null : this.dbl(); // 如果最大值为 1则不需要计算倍点
return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step);
};
// 生成 NAF 点
for (var i = 1; i < max; i++)
res[i] = res[i - 1].add(dbl); // 当前点加上倍点,得到下一个点
BasePoint.prototype._getDoubles = function _getDoubles(step, power) {
if (this.precomputed && this.precomputed.doubles)
return this.precomputed.doubles;
return {
wnd: wnd, // 返回窗口大小
points: res, // 返回 NAF 点数组
};
var doubles = [ this ];
var acc = this;
for (var i = 0; i < power; i += step) {
for (var j = 0; j < step; j++)
acc = acc.dbl();
doubles.push(acc);
}
return {
step: step,
points: doubles,
};
};
// 获取 beta 值的函数,当前未实现
BasePoint.prototype._getBeta = function _getBeta() {
return null; // 返回 null表示未实现
BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) {
if (this.precomputed && this.precomputed.naf)
return this.precomputed.naf;
var res = [ this ];
var max = (1 << wnd) - 1;
var dbl = max === 1 ? null : this.dbl();
for (var i = 1; i < max; i++)
res[i] = res[i - 1].add(dbl);
return {
wnd: wnd,
points: res,
};
};
// 点的倍增操作,倍增 k 次
BasePoint.prototype.dblp = function dblp(k) {
var r = this; // 初始化结果为当前点
for (var i = 0; i < k; i++)
r = r.dbl(); // 重复倍增操作
return r; // 返回 k 倍的点
};
BasePoint.prototype._getBeta = function _getBeta() {
return null;
};
BasePoint.prototype.dblp = function dblp(k) {
var r = this;
for (var i = 0; i < k; i++)
r = r.dbl();
return r;
};

@ -1,544 +1,435 @@
'use strict';
var utils = require('../utils'); // 引入工具模块
var BN = require('bn.js'); // 引入大数库
var inherits = require('inherits'); // 引入继承库
var Base = require('./base'); // 引入基类
var utils = require('../utils');
var BN = require('bn.js');
var inherits = require('inherits');
var Base = require('./base');
var assert = utils.assert; // 引入断言工具
var assert = utils.assert;
// 定义 EdwardsCurve 类
function EdwardsCurve(conf) {
// 检查曲线是否是扭曲的
// NOTE: Important as we are creating point in Base.call()
this.twisted = (conf.a | 0) !== 1;
this.mOneA = this.twisted && (conf.a | 0) === -1; // 检查参数 a 是否为 -1
this.extended = this.mOneA; // 是否使用扩展形式
this.mOneA = this.twisted && (conf.a | 0) === -1;
this.extended = this.mOneA;
Base.call(this, 'edwards', conf); // 调用基类构造函数
Base.call(this, 'edwards', conf);
// 初始化曲线参数
this.a = new BN(conf.a, 16).umod(this.red.m); // 将 a 转换为大数并取模
this.a = this.a.toRed(this.red); // 转换为红色形式
this.c = new BN(conf.c, 16).toRed(this.red); // 转换 c
this.c2 = this.c.redSqr(); // 计算 c 的平方
this.d = new BN(conf.d, 16).toRed(this.red); // 转换 d
this.dd = this.d.redAdd(this.d); // 计算 2d
this.a = new BN(conf.a, 16).umod(this.red.m);
this.a = this.a.toRed(this.red);
this.c = new BN(conf.c, 16).toRed(this.red);
this.c2 = this.c.redSqr();
this.d = new BN(conf.d, 16).toRed(this.red);
this.dd = this.d.redAdd(this.d);
// 断言确保曲线参数的合法性
assert(!this.twisted || this.c.fromRed().cmpn(1) === 0);
this.oneC = (conf.c | 0) === 1; // 检查 c 是否为 1
this.oneC = (conf.c | 0) === 1;
}
inherits(EdwardsCurve, Base); // 继承 Base 类
module.exports = EdwardsCurve; // 导出 EdwardsCurve 类
inherits(EdwardsCurve, Base);
module.exports = EdwardsCurve;
// 计算 a 与 num 的乘积
EdwardsCurve.prototype._mulA = function _mulA(num) {
if (this.mOneA)
return num.redNeg(); // 如果 mOneA 为 true返回 num 的相反数
return num.redNeg();
else
return this.a.redMul(num); // 否则返回 a 与 num 的乘积
return this.a.redMul(num);
};
// 计算 c 与 num 的乘积
EdwardsCurve.prototype._mulC = function _mulC(num) {
if (this.oneC)
return num; // 如果 c 为 1返回 num
return num;
else
return this.c.redMul(num); // 否则返回 c 与 num 的乘积
return this.c.redMul(num);
};
// 为了与短曲线兼容,返回点
// Just for compatibility with Short curve
EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) {
return this.point(x, y, z, t); // 返回点
return this.point(x, y, z, t);
};
// 从 x 坐标生成点
EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) {
x = new BN(x, 16); // 将 x 转换为大数
x = new BN(x, 16);
if (!x.red)
x = x.toRed(this.red); // 转换为红色形式
x = x.toRed(this.red);
var x2 = x.redSqr(); // 计算 x 的平方
var rhs = this.c2.redSub(this.a.redMul(x2)); // 计算右侧值
var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); // 计算左侧值
var x2 = x.redSqr();
var rhs = this.c2.redSub(this.a.redMul(x2));
var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2));
var y2 = rhs.redMul(lhs.redInvm()); // 计算 y^2
var y = y2.redSqrt(); // 计算 y
// 验证 y 是否有效
var y2 = rhs.redMul(lhs.redInvm());
var y = y2.redSqrt();
if (y.redSqr().redSub(y2).cmp(this.zero) !== 0)
throw new Error('invalid point'); // 如果无效,抛出错误
throw new Error('invalid point');
var isOdd = y.fromRed().isOdd(); // 检查 y 是否为奇数
var isOdd = y.fromRed().isOdd();
if (odd && !isOdd || !odd && isOdd)
y = y.redNeg(); // 根据 odd 参数调整 y 的符号
y = y.redNeg();
return this.point(x, y); // 返回生成的点
return this.point(x, y);
};
// 从 y 坐标生成点
EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) {
y = new BN(y, 16); // 将 y 转换为大数
y = new BN(y, 16);
if (!y.red)
y = y.toRed(this.red); // 转换为红色形式
y = y.toRed(this.red);
// 计算 x^2
// x^2 = (y^2 - c^2) / (c^2 d y^2 - a)
var y2 = y.redSqr();
var lhs = y2.redSub(this.c2); // 计算左侧
var rhs = y2.redMul(this.d).redMul(this.c2).redSub(this.a); // 计算右侧
var x2 = lhs.redMul(rhs.redInvm()); // 计算 x^2
var lhs = y2.redSub(this.c2);
var rhs = y2.redMul(this.d).redMul(this.c2).redSub(this.a);
var x2 = lhs.redMul(rhs.redInvm());
// 处理特殊情况
if (x2.cmp(this.zero) === 0) {
if (odd)
throw new Error('invalid point'); // 如果 odd 为真,抛出错误
throw new Error('invalid point');
else
return this.point(this.zero, y); // 返回 (0, y)
return this.point(this.zero, y);
}
var x = x2.redSqrt(); // 计算 x
var x = x2.redSqrt();
if (x.redSqr().redSub(x2).cmp(this.zero) !== 0)
throw new Error('invalid point'); // 验证 x 是否有效
throw new Error('invalid point');
// 根据 odd 参数调整 x 的符号
if (x.fromRed().isOdd() !== odd)
x = x.redNeg();
return this.point(x, y); // 返回生成的点
return this.point(x, y);
};
// 验证点的有效性
EdwardsCurve.prototype.validate = function validate(point) {
if (point.isInfinity())
return true; // 如果点是无穷大,返回 true
return true;
point.normalize(); // 规范化点
// Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2)
point.normalize();
// 曲线方程: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2)
var x2 = point.x.redSqr(); // 计算 x^2
var y2 = point.y.redSqr(); // 计算 y^2
var lhs = x2.redMul(this.a).redAdd(y2); // 计算左侧
var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); // 计算右侧
var x2 = point.x.redSqr();
var y2 = point.y.redSqr();
var lhs = x2.redMul(this.a).redAdd(y2);
var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2)));
return lhs.cmp(rhs) === 0; // 比较左右两侧是否相等
return lhs.cmp(rhs) === 0;
};
'use strict';
var utils = require('../utils'); // 引入工具模块
var BN = require('bn.js'); // 引入大数库
var inherits = require('inherits'); // 引入继承库
var Base = require('./base'); // 引入基类
var assert = utils.assert; // 引入断言工具
// 定义 EdwardsCurve 类
function EdwardsCurve(conf) {
// 检查曲线是否是扭曲的
this.twisted = (conf.a | 0) !== 1;
this.mOneA = this.twisted && (conf.a | 0) === -1; // 检查参数 a 是否为 -1
this.extended = this.mOneA; // 是否使用扩展形式
Base.call(this, 'edwards', conf); // 调用基类构造函数
// 初始化曲线参数
this.a = new BN(conf.a, 16).umod(this.red.m); // 将 a 转换为大数并取模
this.a = this.a.toRed(this.red); // 转换为红色形式
this.c = new BN(conf.c, 16).toRed(this.red); // 转换 c
this.c2 = this.c.redSqr(); // 计算 c 的平方
this.d = new BN(conf.d, 16).toRed(this.red); // 转换 d
this.dd = this.d.redAdd(this.d); // 计算 2d
// 断言确保曲线参数的合法性
assert(!this.twisted || this.c.fromRed().cmpn(1) === 0);
this.oneC = (conf.c | 0) === 1; // 检查 c 是否为 1
function Point(curve, x, y, z, t) {
Base.BasePoint.call(this, curve, 'projective');
if (x === null && y === null && z === null) {
this.x = this.curve.zero;
this.y = this.curve.one;
this.z = this.curve.one;
this.t = this.curve.zero;
this.zOne = true;
} else {
this.x = new BN(x, 16);
this.y = new BN(y, 16);
this.z = z ? new BN(z, 16) : this.curve.one;
this.t = t && new BN(t, 16);
if (!this.x.red)
this.x = this.x.toRed(this.curve.red);
if (!this.y.red)
this.y = this.y.toRed(this.curve.red);
if (!this.z.red)
this.z = this.z.toRed(this.curve.red);
if (this.t && !this.t.red)
this.t = this.t.toRed(this.curve.red);
this.zOne = this.z === this.curve.one;
// Use extended coordinates
if (this.curve.extended && !this.t) {
this.t = this.x.redMul(this.y);
if (!this.zOne)
this.t = this.t.redMul(this.z.redInvm());
}
}
}
inherits(EdwardsCurve, Base); // 继承 Base 类
module.exports = EdwardsCurve; // 导出 EdwardsCurve 类
inherits(Point, Base.BasePoint);
// 计算 a 与 num 的乘积
EdwardsCurve.prototype._mulA = function _mulA(num) {
if (this.mOneA)
return num.redNeg(); // 如果 mOneA 为 true返回 num 的相反数
else
return this.a.redMul(num); // 否则返回 a 与 num 的乘积
EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) {
return Point.fromJSON(this, obj);
};
// 计算 c 与 num 的乘积
EdwardsCurve.prototype._mulC = function _mulC(num) {
if (this.oneC)
return num; // 如果 c 为 1返回 num
else
return this.c.redMul(num); // 否则返回 c 与 num 的乘积
EdwardsCurve.prototype.point = function point(x, y, z, t) {
return new Point(this, x, y, z, t);
};
// 为了与短曲线兼容,返回点
EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) {
return this.point(x, y, z, t); // 返回点
Point.fromJSON = function fromJSON(curve, obj) {
return new Point(curve, obj[0], obj[1], obj[2]);
};
// 从 x 坐标生成点
EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) {
x = new BN(x, 16); // 将 x 转换为大数
if (!x.red)
x = x.toRed(this.red); // 转换为红色形式
var x2 = x.redSqr(); // 计算 x 的平方
var rhs = this.c2.redSub(this.a.redMul(x2)); // 计算右侧值
var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); // 计算左侧值
var y2 = rhs.redMul(lhs.redInvm()); // 计算 y^2
var y = y2.redSqrt(); // 计算 y
// 验证 y 是否有效
if (y.redSqr().redSub(y2).cmp(this.zero) !== 0)
throw new Error('invalid point'); // 如果无效,抛出错误
var isOdd = y.fromRed().isOdd(); // 检查 y 是否为奇数
if (odd && !isOdd || !odd && isOdd)
y = y.redNeg(); // 根据 odd 参数调整 y 的符号
return this.point(x, y); // 返回生成的点
Point.prototype.inspect = function inspect() {
if (this.isInfinity())
return '<EC Point Infinity>';
return '<EC Point x: ' + this.x.fromRed().toString(16, 2) +
' y: ' + this.y.fromRed().toString(16, 2) +
' z: ' + this.z.fromRed().toString(16, 2) + '>';
};
// 从 y 坐标生成点
EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) {
y = new BN(y, 16); // 将 y 转换为大数
if (!y.red)
y = y.toRed(this.red); // 转换为红色形式
// 计算 x^2
var y2 = y.redSqr();
var lhs = y2.redSub(this.c2); // 计算左侧
var rhs = y2.redMul(this.d).redMul(this.c2).redSub(this.a); // 计算右侧
var x2 = lhs.redMul(rhs.redInvm()); // 计算 x^2
// 处理特殊情况
if (x2.cmp(this.zero) === 0) {
if (odd)
throw new Error('invalid point'); // 如果 odd 为真,抛出错误
else
return this.point(this.zero, y); // 返回 (0, y)
}
var x = x2.redSqrt(); // 计算 x
if (x.redSqr().redSub(x2).cmp(this.zero) !== 0)
throw new Error('invalid point'); // 验证 x 是否有效
// 根据 odd 参数调整 x 的符号
if (x.fromRed().isOdd() !== odd)
x = x.redNeg();
return this.point(x, y); // 返回生成的点
Point.prototype.isInfinity = function isInfinity() {
// XXX This code assumes that zero is always zero in red
return this.x.cmpn(0) === 0 &&
(this.y.cmp(this.z) === 0 ||
(this.zOne && this.y.cmp(this.curve.c) === 0));
};
// 验证点的有效性
EdwardsCurve.prototype.validate = function validate(point) {
if (point.isInfinity())
return true; // 如果点是无穷大,返回 true
point.normalize(); // 规范化点
// 曲线方程: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2)
var x2 = point.x.redSqr(); // 计算 x^2
var y2 = point.y.redSqr(); // 计算 y^2
var lhs = x2.redMul(this.a).redAdd(y2); // 计算左侧
var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); // 计算右侧
return lhs.cmp(rhs) === 0; // 比较左右两侧是否相等
Point.prototype._extDbl = function _extDbl() {
// hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
// #doubling-dbl-2008-hwcd
// 4M + 4S
// A = X1^2
var a = this.x.redSqr();
// B = Y1^2
var b = this.y.redSqr();
// C = 2 * Z1^2
var c = this.z.redSqr();
c = c.redIAdd(c);
// D = a * A
var d = this.curve._mulA(a);
// E = (X1 + Y1)^2 - A - B
var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b);
// G = D + B
var g = d.redAdd(b);
// F = G - C
var f = g.redSub(c);
// H = D - B
var h = d.redSub(b);
// X3 = E * F
var nx = e.redMul(f);
// Y3 = G * H
var ny = g.redMul(h);
// T3 = E * H
var nt = e.redMul(h);
// Z3 = F * G
var nz = f.redMul(g);
return this.curve.point(nx, ny, nz, nt);
};
// 在投影坐标系中执行点的倍增
Point.prototype._projDbl = function _projDbl() {
// 参考文献: hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
// hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
// #doubling-dbl-2008-bbjlp
// #doubling-dbl-2007-bl
// 一般需要 3M + 4S 或 2M + 4S 的运算
// and others
// Generally 3M + 4S or 2M + 4S
// B = (X1 + Y1)^2
var b = this.x.redAdd(this.y).redSqr(); // 计算 B
var b = this.x.redAdd(this.y).redSqr();
// C = X1^2
var c = this.x.redSqr(); // 计算 C
var c = this.x.redSqr();
// D = Y1^2
var d = this.y.redSqr(); // 计算 D
var nx; // 新点的 x 坐标
var ny; // 新点的 y 坐标
var nz; // 新点的 z 坐标
var e; // 中间变量
var h; // 中间变量
var j; // 中间变量
if (this.curve.twisted) { // 如果曲线是扭曲的
var d = this.y.redSqr();
var nx;
var ny;
var nz;
var e;
var h;
var j;
if (this.curve.twisted) {
// E = a * C
e = this.curve._mulA(c); // 计算 E
e = this.curve._mulA(c);
// F = E + D
var f = e.redAdd(d); // 计算 F
if (this.zOne) { // 如果 z 坐标为 1
var f = e.redAdd(d);
if (this.zOne) {
// X3 = (B - C - D) * (F - 2)
nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two)); // 计算新点的 x 坐标
nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two));
// Y3 = F * (E - D)
ny = f.redMul(e.redSub(d)); // 计算新点的 y 坐标
ny = f.redMul(e.redSub(d));
// Z3 = F^2 - 2 * F
nz = f.redSqr().redSub(f).redSub(f); // 计算新点的 z 坐标
nz = f.redSqr().redSub(f).redSub(f);
} else {
// H = Z1^2
h = this.z.redSqr(); // 计算 H
h = this.z.redSqr();
// J = F - 2 * H
j = f.redSub(h).redISub(h); // 计算 J
j = f.redSub(h).redISub(h);
// X3 = (B-C-D)*J
nx = b.redSub(c).redISub(d).redMul(j); // 计算新点的 x 坐标
nx = b.redSub(c).redISub(d).redMul(j);
// Y3 = F * (E - D)
ny = f.redMul(e.redSub(d)); // 计算新点的 y 坐标
ny = f.redMul(e.redSub(d));
// Z3 = F * J
nz = f.redMul(j); // 计算新点的 z 坐标
nz = f.redMul(j);
}
} else { // 如果曲线不是扭曲的
} else {
// E = C + D
e = c.redAdd(d); // 计算 E
e = c.redAdd(d);
// H = (c * Z1)^2
h = this.curve._mulC(this.z).redSqr(); // 计算 H
h = this.curve._mulC(this.z).redSqr();
// J = E - 2 * H
j = e.redSub(h).redSub(h); // 计算 J
j = e.redSub(h).redSub(h);
// X3 = c * (B - E) * J
nx = this.curve._mulC(b.redISub(e)).redMul(j); // 计算新点的 x 坐标
nx = this.curve._mulC(b.redISub(e)).redMul(j);
// Y3 = c * E * (C - D)
ny = this.curve._mulC(e).redMul(c.redISub(d)); // 计算新点的 y 坐标
ny = this.curve._mulC(e).redMul(c.redISub(d));
// Z3 = E * J
nz = e.redMul(j); // 计算新点的 z 坐标
nz = e.redMul(j);
}
return this.curve.point(nx, ny, nz); // 返回新计算的点
return this.curve.point(nx, ny, nz);
};
// 点的倍增方法
Point.prototype.dbl = function dbl() {
if (this.isInfinity()) // 如果点是无穷大,直接返回
if (this.isInfinity())
return this;
// 在扩展坐标下倍增
// Double in extended coordinates
if (this.curve.extended)
return this._extDbl(); // 调用扩展坐标倍增方法
return this._extDbl();
else
return this._projDbl(); // 调用投影坐标倍增方法
return this._projDbl();
};
// 在扩展坐标系中执行点的加法
Point.prototype._extAdd = function _extAdd(p) {
// 参考文献: hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
// hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
// #addition-add-2008-hwcd-3
// 需要 8M 的运算
// 8M
// A = (Y1 - X1) * (Y2 - X2)
var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x)); // 计算 A
var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x));
// B = (Y1 + X1) * (Y2 + X2)
var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x)); // 计算 B
var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x));
// C = T1 * k * T2
var c = this.t.redMul(this.curve.dd).redMul(p.t); // 计算 C
var c = this.t.redMul(this.curve.dd).redMul(p.t);
// D = Z1 * 2 * Z2
var d = this.z.redMul(p.z.redAdd(p.z)); // 计算 D
var d = this.z.redMul(p.z.redAdd(p.z));
// E = B - A
var e = b.redSub(a); // 计算 E
var e = b.redSub(a);
// F = D - C
var f = d.redSub(c); // 计算 F
var f = d.redSub(c);
// G = D + C
var g = d.redAdd(c); // 计算 G
var g = d.redAdd(c);
// H = B + A
var h = b.redAdd(a); // 计算 H
var h = b.redAdd(a);
// X3 = E * F
var nx = e.redMul(f); // 计算新点的 x 坐标
var nx = e.redMul(f);
// Y3 = G * H
var ny = g.redMul(h); // 计算新点的 y 坐标
var ny = g.redMul(h);
// T3 = E * H
var nt = e.redMul(h); // 计算新点的 t 坐标
var nt = e.redMul(h);
// Z3 = F * G
var nz = f.redMul(g); // 计算新点的 z 坐标
return this.curve.point(nx, ny, nz, nt); // 返回新计算的点
var nz = f.redMul(g);
return this.curve.point(nx, ny, nz, nt);
};
// 在投影坐标系中执行点的加法
Point.prototype._projAdd = function _projAdd(p) {
// 参考文献: hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
// hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
// #addition-add-2008-bbjlp
// #addition-add-2007-bl
// 计算复杂度: 10M + 1S
// 10M + 1S
// A = Z1 * Z2
var a = this.z.redMul(p.z); // 计算 A两个点的 z 坐标的乘积
var a = this.z.redMul(p.z);
// B = A^2
var b = a.redSqr(); // 计算 BA 的平方
var b = a.redSqr();
// C = X1 * X2
var c = this.x.redMul(p.x); // 计算 C两个点的 x 坐标的乘积
var c = this.x.redMul(p.x);
// D = Y1 * Y2
var d = this.y.redMul(p.y); // 计算 D两个点的 y 坐标的乘积
var d = this.y.redMul(p.y);
// E = d * C * D
var e = this.curve.d.redMul(c).redMul(d); // 计算 E涉及曲线参数 d
var e = this.curve.d.redMul(c).redMul(d);
// F = B - E
var f = b.redSub(e); // 计算 F
var f = b.redSub(e);
// G = B + E
var g = b.redAdd(e); // 计算 G
var g = b.redAdd(e);
// X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D)
var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d; // 计算 tmp
var nx = a.redMul(f).redMul(tmp); // 计算新点的 x 坐标
var ny; // 新点的 y 坐标
var nz; // 新点的 z 坐标
if (this.curve.twisted) { // 如果曲线是扭曲的
var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d);
var nx = a.redMul(f).redMul(tmp);
var ny;
var nz;
if (this.curve.twisted) {
// Y3 = A * G * (D - a * C)
ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); // 计算新点的 y 坐标
ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c)));
// Z3 = F * G
nz = f.redMul(g); // 计算新点的 z 坐标
} else { // 如果曲线不是扭曲的
nz = f.redMul(g);
} else {
// Y3 = A * G * (D - C)
ny = a.redMul(g).redMul(d.redSub(c)); // 计算新点的 y 坐标
ny = a.redMul(g).redMul(d.redSub(c));
// Z3 = c * F * G
nz = this.curve._mulC(f).redMul(g); // 计算新点的 z 坐标
nz = this.curve._mulC(f).redMul(g);
}
return this.curve.point(nx, ny, nz); // 返回新计算的点
return this.curve.point(nx, ny, nz);
};
// 点的加法方法
Point.prototype.add = function add(p) {
if (this.isInfinity()) // 如果当前点是无穷大,返回 p
if (this.isInfinity())
return p;
if (p.isInfinity()) // 如果 p 是无穷大,返回当前点
if (p.isInfinity())
return this;
// 根据曲线的坐标系统调用相应的加法方法
if (this.curve.extended)
return this._extAdd(p); // 调用扩展坐标加法方法
return this._extAdd(p);
else
return this._projAdd(p); // 调用投影坐标加法方法
return this._projAdd(p);
};
// 点的倍乘方法
Point.prototype.mul = function mul(k) {
// 如果 k 有重复的倍增操作,使用固定的 NAF 乘法
if (this._hasDoubles(k))
return this.curve._fixedNafMul(this, k);
else // 否则使用 WNAF 乘法
else
return this.curve._wnafMul(this, k);
};
// 进行加法的倍乘方法
Point.prototype.mulAdd = function mulAdd(k1, p, k2) {
// 使用 WNAF 乘法加法,返回结果
return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false);
};
// 在 Jacobian 坐标系下进行加法的倍乘方法
Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) {
// 使用 WNAF 乘法加法,返回结果
return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true);
};
// 归一化点的坐标
Point.prototype.normalize = function normalize() {
if (this.zOne) // 如果 z 坐标为 1直接返回当前点
if (this.zOne)
return this;
// 计算 z 的逆
// Normalize coordinates
var zi = this.z.redInvm();
this.x = this.x.redMul(zi); // 归一化 x 坐标
this.y = this.y.redMul(zi); // 归一化 y 坐标
if (this.t) // 如果有 t 坐标
this.t = this.t.redMul(zi); // 归一化 t 坐标
this.z = this.curve.one; // 将 z 坐标设为 1
this.zOne = true; // 设置标志为 true
return this; // 返回归一化后的点
this.x = this.x.redMul(zi);
this.y = this.y.redMul(zi);
if (this.t)
this.t = this.t.redMul(zi);
this.z = this.curve.one;
this.zOne = true;
return this;
};
// 在投影坐标系中执行点的加法
Point.prototype._projAdd = function _projAdd(p) {
// 参考文献: hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
// #addition-add-2008-bbjlp
// #addition-add-2007-bl
// 计算复杂度: 10M + 1S
// A = Z1 * Z2
var a = this.z.redMul(p.z); // 计算 A两个点的 z 坐标的乘积
// B = A^2
var b = a.redSqr(); // 计算 BA 的平方
// C = X1 * X2
var c = this.x.redMul(p.x); // 计算 C两个点的 x 坐标的乘积
// D = Y1 * Y2
var d = this.y.redMul(p.y); // 计算 D两个点的 y 坐标的乘积
// E = d * C * D
var e = this.curve.d.redMul(c).redMul(d); // 计算 E涉及曲线参数 d
// F = B - E
var f = b.redSub(e); // 计算 F
// G = B + E
var g = b.redAdd(e); // 计算 G
// X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D)
var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d; // 计算 tmp
var nx = a.redMul(f).redMul(tmp); // 计算新点的 x 坐标
var ny; // 新点的 y 坐标
var nz; // 新点的 z 坐标
if (this.curve.twisted) { // 如果曲线是扭曲的
// Y3 = A * G * (D - a * C)
ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); // 计算新点的 y 坐标
// Z3 = F * G
nz = f.redMul(g); // 计算新点的 z 坐标
} else { // 如果曲线不是扭曲的
// Y3 = A * G * (D - C)
ny = a.redMul(g).redMul(d.redSub(c)); // 计算新点的 y 坐标
// Z3 = c * F * G
nz = this.curve._mulC(f).redMul(g); // 计算新点的 z 坐标
}
return this.curve.point(nx, ny, nz); // 返回新计算的点
Point.prototype.neg = function neg() {
return this.curve.point(this.x.redNeg(),
this.y,
this.z,
this.t && this.t.redNeg());
};
// 点的加法方法
Point.prototype.add = function add(p) {
if (this.isInfinity()) // 如果当前点是无穷大,返回 p
return p;
if (p.isInfinity()) // 如果 p 是无穷大,返回当前点
return this;
// 根据曲线的坐标系统调用相应的加法方法
if (this.curve.extended)
return this._extAdd(p); // 调用扩展坐标加法方法
else
return this._projAdd(p); // 调用投影坐标加法方法
Point.prototype.getX = function getX() {
this.normalize();
return this.x.fromRed();
};
// 点的倍乘方法
Point.prototype.mul = function mul(k) {
// 如果 k 有重复的倍增操作,使用固定的 NAF 乘法
if (this._hasDoubles(k))
return this.curve._fixedNafMul(this, k);
else // 否则使用 WNAF 乘法
return this.curve._wnafMul(this, k);
Point.prototype.getY = function getY() {
this.normalize();
return this.y.fromRed();
};
// 进行加法的倍乘方法
Point.prototype.mulAdd = function mulAdd(k1, p, k2) {
// 使用 WNAF 乘法加法,返回结果
return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false);
Point.prototype.eq = function eq(other) {
return this === other ||
this.getX().cmp(other.getX()) === 0 &&
this.getY().cmp(other.getY()) === 0;
};
// 在 Jacobian 坐标系下进行加法的倍乘方法
Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) {
// 使用 WNAF 乘法加法,返回结果
return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true);
Point.prototype.eqXToP = function eqXToP(x) {
var rx = x.toRed(this.curve.red).redMul(this.z);
if (this.x.cmp(rx) === 0)
return true;
var xc = x.clone();
var t = this.curve.redN.redMul(this.z);
for (;;) {
xc.iadd(this.curve.n);
if (xc.cmp(this.curve.p) >= 0)
return false;
rx.redIAdd(t);
if (this.x.cmp(rx) === 0)
return true;
}
};
// 归一化点的坐标
Point.prototype.normalize = function normalize() {
if (this.zOne) // 如果 z 坐标为 1直接返回当前点
return this;
// 计算 z 的逆
var zi = this.z.redInvm();
this.x = this.x.redMul(zi); // 归一化 x 坐标
this.y = this.y.redMul(zi); // 归一化 y 坐标
if (this.t) // 如果有 t 坐标
this.t = this.t.redMul(zi); // 归一化 t 坐标
this.z = this.curve.one; // 将 z 坐标设为 1
this.zOne = true; // 设置标志为 true
return this; // 返回归一化后的点
};
// Compatibility with BaseCurve
Point.prototype.toP = Point.prototype.normalize;
Point.prototype.mixedAdd = Point.prototype.add;

@ -1,107 +1,72 @@
'use strict';
// 引入大整数库bn.js用于处理大整数运算
var BN = require('bn.js');
// 用于实现JavaScript中的继承机制
var inherits = require('inherits');
// 引入Base类可能是一个基础的抽象类或者包含了一些通用的功能具体要看其定义
var Base = require('./base');
// 引入一些工具函数,具体功能要看其内部实现
var utils = require('../utils');
// MontCurve类代表蒙哥马利曲线继承自Base类
function MontCurve(conf) {
// 调用Base类的构造函数传入曲线类型'mont'和配置信息conf
Base.call(this,'mont', conf);
Base.call(this, 'mont', conf);
// 将十六进制表示的配置参数conf.a转换为大整数并转换到蒙哥马利域通过toRed方法this.red应该是蒙哥马利域相关的设置
this.a = new BN(conf.a, 16).toRed(this.red);
// 同理处理配置参数conf.b
this.b = new BN(conf.b, 16).toRed(this.red);
// 将整数4转换为大整数并转换到蒙哥马利域然后求其乘法逆元在蒙哥马利域内
this.i4 = new BN(4).toRed(this.red).redInvm();
// 将整数2转换为大整数并转换到蒙哥马利域
this.two = new BN(2).toRed(this.red);
// 计算a24具体计算公式看代码逻辑涉及到前面计算的一些值
this.a24 = this.i4.redMul(this.a.redAdd(this.two));
}
// 实现MontCurve类继承自Base类的继承关系
inherits(MontCurve, Base);
// 将MontCurve类作为模块对外暴露方便其他模块使用
module.exports = MontCurve;
// MontCurve类的实例方法用于验证一个点是否在蒙哥马利曲线上
MontCurve.prototype.validate = function validate(point) {
// 获取点的横坐标x并进行归一化处理具体看normalize方法的实现
var x = point.normalize().x;
// 计算横坐标x的平方在蒙哥马利域内
var x2 = x.redSqr();
// 按照蒙哥马利曲线的方程右边部分进行计算(涉及乘法、加法等运算,都是在蒙哥马利域内)
var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x);
// 计算rhs的平方根在蒙哥马利域内
var y = rhs.redSqrt();
// 比较y的平方和rhs是否相等以此判断点是否在曲线上返回比较结果0表示相等
return y.redSqr().cmp(rhs) === 0;
};
// Point类代表曲线上的点继承自Base.BasePoint类具体要看Base.BasePoint的定义
function Point(curve, x, z) {
// 调用Base.BasePoint类的构造函数传入曲线类型和'projective'标识,具体作用看其定义
Base.BasePoint.call(this, curve, 'projective');
if (x === null && z === null) {
// 如果传入的横坐标x和竖坐标z都为null将横坐标设为曲线的单位元通常表示无穷远点相关概念
this.x = this.curve.one;
// 将竖坐标设为曲线的零元素
this.z = this.curve.zero;
} else {
// 将传入的十六进制表示的横坐标x转换为大整数
this.x = new BN(x, 16);
// 将传入的十六进制表示的竖坐标z转换为大整数
this.z = new BN(z, 16);
if (!this.x.red)
// 如果横坐标的大整数还未转换到蒙哥马利域进行转换通过曲线的相关设置this.curve.red
this.x = this.x.toRed(this.curve.red);
if (!this.z.red)
// 同理,对竖坐标进行转换到蒙哥马利域的操作
this.z = this.z.toRed(this.curve.red);
}
}
// 实现Point类继承自Base.BasePoint类的继承关系
inherits(Point, Base.BasePoint);
// MontCurve类的实例方法用于从字节数据解码出曲线上的一个点根据给定的编码格式enc
MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) {
return this.point(utils.toArray(bytes, enc), 1);
};
// MontCurve类的实例方法用于创建一个曲线上的点对象传入横坐标x和竖坐标z或默认值
MontCurve.prototype.point = function point(x, z) {
return new Point(this, x, z);
};
// MontCurve类的实例方法用于从JSON数据创建一个曲线上的点对象具体要看JSON数据的格式要求等
MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) {
return Point.fromJSON(this, obj);
};
// Point类的实例方法预计算相关目前这里是无操作可能后续会添加具体逻辑
Point.prototype.precompute = function precompute() {
// No-op
};
// Point类的实例方法用于将点编码为字节数组大端序长度根据曲线的某个属性curve.p.byteLength()确定)
Point.prototype._encode = function _encode() {
return this.getX().toArray('be', this.curve.p.byteLength());
};
// Point类的静态方法用于从JSON数据创建一个Point对象传入曲线对象和JSON数据
Point.fromJSON = function fromJSON(curve, obj) {
return new Point(curve, obj[0], obj[1] || curve.one);
};
// Point类的实例方法用于返回点的字符串表示形式方便调试等查看
Point.prototype.inspect = function inspect() {
if (this.isInfinity())
return '<EC Point Infinity>';
@ -109,115 +74,105 @@ Point.prototype.inspect = function inspect() {
' z: ' + this.z.fromRed().toString(16, 2) + '>';
};
// Point类的实例方法用于判断点是否为无穷远点通过比较竖坐标是否为0来判断这里假设在蒙哥马利域内零的表示是固定的
Point.prototype.isInfinity = function isInfinity() {
// XXX This code assumes that zero is always zero in red
return this.z.cmpn(0) === 0;
};
// Point类的实例方法用于实现点的加倍操作按照蒙哥马利曲线的相关算法实现参考了特定的文档链接
Point.prototype.dbl = function dbl() {
// http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3
// 2M + 2S + 4A,表示此操作涉及到的乘法、平方和加法的次数(大概是性能评估相关的注释)
// 2M + 2S + 4A
// A = X1 + Z1,计算横坐标和竖坐标的和(在蒙哥马利域内)
// A = X1 + Z1
var a = this.x.redAdd(this.z);
// AA = A^2计算a的平方在蒙哥马利域内
// AA = A^2
var aa = a.redSqr();
// B = X1 - Z1,计算横坐标和竖坐标的差(在蒙哥马利域内)
// B = X1 - Z1
var b = this.x.redSub(this.z);
// BB = B^2计算b的平方在蒙哥马利域内
// BB = B^2
var bb = b.redSqr();
// C = AA - BB计算aa和bb的差在蒙哥马利域内
// C = AA - BB
var c = aa.redSub(bb);
// X3 = AA * BB,计算新的横坐标(在蒙哥马利域内)
// X3 = AA * BB
var nx = aa.redMul(bb);
// Z3 = C * (BB + A24 * C),计算新的竖坐标(在蒙哥马利域内)
// Z3 = C * (BB + A24 * C)
var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c)));
return this.curve.point(nx, nz);
};
// Point类的实例方法加法操作这里直接抛出不支持的错误说明蒙哥马利曲线可能不支持这种简单的加法形式
Point.prototype.add = function add() {
throw new Error('Not supported on Montgomery curve');
};
// Point类的实例方法差分加法操作按照蒙哥马利曲线的相关算法实现参考了特定的文档链接
Point.prototype.diffAdd = function diffAdd(p, diff) {
// http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3
// 4M + 2S + 6A,表示此操作涉及到的乘法、平方和加法的次数(大概是性能评估相关的注释)
// 4M + 2S + 6A
// A = X2 + Z2,计算当前点的横坐标和竖坐标的和(在蒙哥马利域内)
// A = X2 + Z2
var a = this.x.redAdd(this.z);
// B = X2 - Z2,计算当前点的横坐标和竖坐标的差(在蒙哥马利域内)
// B = X2 - Z2
var b = this.x.redSub(this.z);
// C = X3 + Z3计算传入点p的横坐标和竖坐标的和在蒙哥马利域内
// C = X3 + Z3
var c = p.x.redAdd(p.z);
// D = X3 - Z3计算传入点p的横坐标和竖坐标的差在蒙哥马利域内
// D = X3 - Z3
var d = p.x.redSub(p.z);
// DA = D * A计算d和a的乘积在蒙哥马利域内
// DA = D * A
var da = d.redMul(a);
// CB = C * B计算c和b的乘积在蒙哥马利域内
// CB = C * B
var cb = c.redMul(b);
// X5 = Z1 * (DA + CB)^2,计算新的横坐标(在蒙哥马利域内)
// X5 = Z1 * (DA + CB)^2
var nx = diff.z.redMul(da.redAdd(cb).redSqr());
// Z5 = X1 * (DA - CB)^2,计算新的竖坐标(在蒙哥马利域内)
// Z5 = X1 * (DA - CB)^2
var nz = diff.x.redMul(da.redISub(cb).redSqr());
return this.curve.point(nx, nz);
};
// Point类的实例方法点乘操作实现了一种基于二进制分解的点乘算法具体逻辑看代码内循环等操作
Point.prototype.mul = function mul(k) {
var t = k.clone();
var a = this; // (N / 2) * Q + Q,表示当前点相关的一种中间状态
var b = this.curve.point(null, null); // (N / 2) * Q,表示另一种中间状态
var c = this; // Q,表示初始的点
var a = this; // (N / 2) * Q + Q
var b = this.curve.point(null, null); // (N / 2) * Q
var c = this; // Q
for (var bits = []; t.cmpn(0)!== 0; t.iushrn(1))
for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1))
bits.push(t.andln(1));
for (var i = bits.length - 1; i >= 0; i--) {
if (bits[i] === 0) {
// N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q,根据二进制位进行相应的点运算更新
// N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q
a = a.diffAdd(b, c);
// N * Q = 2 * ((N / 2) * Q + Q)),更新中间状态
// N * Q = 2 * ((N / 2) * Q + Q))
b = b.dbl();
} else {
// N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q),根据二进制位进行相应的点运算更新
// N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q)
b = a.diffAdd(b, c);
// N * Q + Q = 2 * ((N / 2) * Q + Q),更新中间状态
// N * Q + Q = 2 * ((N / 2) * Q + Q)
a = a.dbl();
}
}
return b;
};
// Point类的实例方法乘法加法操作这里直接抛出不支持的错误说明蒙哥马利曲线可能不支持这种操作形式
Point.prototype.mulAdd = function mulAdd() {
throw new Error('Not supported on Montgomery curve');
};
// Point类的实例方法jumlAdd操作具体不清楚是什么操作这里直接抛出不支持的错误
Point.prototype.jumlAdd = function jumlAdd() {
throw new Error('Not supported on Montgomery curve');
};
// Point类的实例方法用于比较两个点是否相等通过比较横坐标是否相等来判断
Point.prototype.eq = function eq(other) {
return this.getX().cmp(other.getX()) === 0;
};
// Point类的实例方法用于对坐标进行归一化处理将横坐标乘以竖坐标的逆元竖坐标设为曲线的单位元
Point.prototype.normalize = function normalize() {
this.x = this.x.redMul(this.z.redInvm());
this.z = this.curve.one;
return this;
};
// Point类的实例方法用于获取归一化后的横坐标先进行归一化处理再从蒙哥马利域转换出来
Point.prototype.getX = function getX() {
// Normalize coordinates
this.normalize();
return this.x.fromRed();
};
};

File diff suppressed because it is too large Load Diff

@ -1,19 +1,14 @@
'use strict';
// 导出的对象
var curves = exports;
// 引入所需的模块
var hash = require('hash.js');
var curve = require('./curve');
var utils = require('./utils');
// 引入断言函数
var assert = utils.assert;
// PresetCurve 构造函数
function PresetCurve(options) {
// 根据 options 的类型创建不同类型的曲线对象
if (options.type === 'short')
this.curve = new curve.short(options);
else if (options.type === 'edwards')
@ -24,20 +19,16 @@ function PresetCurve(options) {
this.n = this.curve.n;
this.hash = options.hash;
// 进行曲线的有效性断言
assert(this.g.validate(), 'Invalid curve');
assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N!= O');
assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N != O');
}
// 将 PresetCurve 函数添加到 curves 对象中
curves.PresetCurve = PresetCurve;
// 定义曲线的函数
function defineCurve(name, options) {
Object.defineProperty(curves, name, {
configurable: true,
enumerable: true,
get: function() {
// 创建 PresetCurve 实例
var curve = new PresetCurve(options);
Object.defineProperty(curves, name, {
configurable: true,
@ -49,7 +40,6 @@ function defineCurve(name, options) {
});
}
// 定义 p192 曲线
defineCurve('p192', {
type: 'short',
prime: 'p192',
@ -65,12 +55,11 @@ defineCurve('p192', {
],
});
// 定义 p224 曲线
defineCurve('p224', {
type: 'short',
prime: 'p224',
p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000000 00000001',
a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe',
p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001',
a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe',
b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4',
n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d',
hash: hash.sha256,
@ -81,7 +70,6 @@ defineCurve('p224', {
],
});
// 定义 p256 曲线
defineCurve('p256', {
type: 'short',
prime: null,
@ -97,18 +85,17 @@ defineCurve('p256', {
],
});
// 定义 p384 曲线
defineCurve('p384', {
type: 'short',
prime: null,
p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'fffffffe ffffffff 00000000 00000000 ffffffff',
'fffffffe ffffffff 00000000 00000000 ffffffff',
a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'fffffffe ffffffff 00000000 00000000 fffffffc',
'fffffffe ffffffff 00000000 00000000 fffffffc',
b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' +
'5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef',
'5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef',
n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' +
'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973',
'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973',
hash: hash.sha384,
gRed: false,
g: [
@ -119,22 +106,21 @@ defineCurve('p384', {
],
});
// 定义 p521 曲线
defineCurve('p521', {
type: 'short',
prime: null,
p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'ffffffff ffffffff ffffffff ffffffff ffffffff',
'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'ffffffff ffffffff ffffffff ffffffff ffffffff',
a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'ffffffff ffffffff ffffffff ffffffff fffffffc',
'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'ffffffff ffffffff ffffffff ffffffff fffffffc',
b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' +
'99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' +
'3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00',
'99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' +
'3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00',
n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' +
'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' +
'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409',
'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' +
'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409',
hash: hash.sha512,
gRed: false,
g: [
@ -147,7 +133,6 @@ defineCurve('p521', {
],
});
// 定义 curve25519 曲线
defineCurve('curve25519', {
type: 'mont',
prime: 'p25519',
@ -162,7 +147,6 @@ defineCurve('curve25519', {
],
});
// 定义 ed25519 曲线
defineCurve('ed25519', {
type: 'edwards',
prime: 'p25519',
@ -182,7 +166,6 @@ defineCurve('ed25519', {
],
});
// 尝试加载预计算的 secp256k1 数据
var pre;
try {
pre = require('./precomputed/secp256k1');
@ -190,7 +173,6 @@ try {
pre = undefined;
}
// 定义 secp256k1 曲线
defineCurve('secp256k1', {
type: 'short',
prime: 'k256',
@ -201,7 +183,7 @@ defineCurve('secp256k1', {
h: '1',
hash: hash.sha256,
// 预计算的自同态数据
// Precomputed endomorphism
beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee',
lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72',
basis: [
@ -221,4 +203,4 @@ defineCurve('secp256k1', {
'483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8',
pre,
],
});
});

@ -1,89 +1,59 @@
'use strict';
// 将当前模块的内容向外暴露,通过 `utils` 对象来提供各种工具函数和属性
var utils = exports;
// 引入 `bn.js` 库,用于处理大整数相关操作
var BN = require('bn.js');
// 引入 `minimalistic-assert` 库,可能用于进行简单的断言操作
var minAssert = require('minimalistic-assert');
// 引入 `minimalistic-crypto-utils` 库,包含一些密码学相关的工具函数
var minUtils = require('minimalistic-crypto-utils');
// 将 `minimalistic-assert` 库中的断言函数挂载到 `utils` 对象上,方便外部调用进行条件判断和错误抛出
utils.assert = minAssert;
// 将 `minimalistic-crypto-utils` 库中的 `toArray` 函数挂载到 `utils` 对象上,可能用于将某些数据转换为数组形式
utils.toArray = minUtils.toArray;
// 挂载 `minimalistic-crypto-utils` 库中的 `zero2` 函数到 `utils` 对象,具体功能需看原函数定义
utils.zero2 = minUtils.zero2;
// 挂载 `minimalistic-crypto-utils` 库中的 `toHex` 函数到 `utils` 对象,可能用于将数据转换为十六进制表示形式
utils.toHex = minUtils.toHex;
// 挂载 `minimalistic-crypto-utils` 库中的 `encode` 函数到 `utils` 对象,其功能取决于原函数实现,可能用于编码相关操作
utils.encode = minUtils.encode;
// 函数 `getNAF` 的功能是将给定的大整数 `num` 表示为宽度为 `w` 的非相邻形式w-NAFNon-Adjacent Form
// 参数:
// - `num`:要转换的大整数(`BN` 类型)
// - `w`:宽度参数,用于控制非相邻形式的表示方式
// - `bits`:位数相关参数,可能用于确定结果数组的长度等
// Represent num in a w-NAF form
function getNAF(num, w, bits) {
// 创建一个数组 `naf`,长度根据 `num` 的位数和传入的 `bits` 参数确定,初始值都填充为 0
var naf = new Array(Math.max(num.bitLength(), bits) + 1);
naf.fill(0);
// 计算一个与宽度 `w` 相关的值 `ws`,用于后续的位运算和计算
var ws = 1 << (w + 1);
// 克隆传入的大整数 `num`,用于后续在不改变原始值的情况下进行操作
var k = num.clone();
// 循环遍历 `naf` 数组的每个位置,进行非相邻形式的转换计算
for (var i = 0; i < naf.length; i++) {
var z;
// 通过位运算获取 `k` 的低 `w + 1` 位的值(取模操作)
var mod = k.andln(ws - 1);
// 如果 `k` 是奇数(最低位为 1
if (k.isOdd()) {
// 根据 `mod` 的值来确定 `z` 的值,以满足非相邻形式的规则
if (mod > (ws >> 1) - 1)
z = (ws >> 1) - mod;
else
z = mod;
// 从 `k` 中减去 `z`
k.isubn(z);
} else {
// 如果 `k` 是偶数,`z` 赋值为 0
z = 0;
}
// 将计算得到的 `z` 值存入 `naf` 数组当前位置
naf[i] = z;
// 将 `k` 向右逻辑移位 1 位(相当于除以 2为下一轮计算做准备
k.iushrn(1);
}
// 返回表示为非相邻形式的数组 `naf`
return naf;
}
// 将 `getNAF` 函数挂载到 `utils` 对象上,方便外部调用
utils.getNAF = getNAF;
// 函数 `getJSF` 的功能是将两个大整数 `k1` 和 `k2` 表示为联合稀疏形式Joint Sparse FormJSF
// Represent k1, k2 in a Joint Sparse Form
function getJSF(k1, k2) {
// 创建一个二维数组 `jsf`,用于存储 `k1` 和 `k2` 的联合稀疏形式表示,初始化为两个空数组
var jsf = [
[],
[],
];
// 克隆传入的大整数 `k1` 和 `k2`,以便在不改变原始值的情况下进行操作
k1 = k1.clone();
k2 = k2.clone();
var d1 = 0;
var d2 = 0;
var m8;
// 循环条件是 `k1` 大于 `-d1` 或者 `k2` 大于 `-d2`,表示还未处理完所有位
while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) {
// 第一阶段:根据 `k1` 和 `k2` 的低几位来确定 `u1` 和 `u2` 的值
// First phase
var m14 = (k1.andln(3) + d1) & 3;
var m24 = (k2.andln(3) + d2) & 3;
if (m14 === 3)
@ -100,7 +70,6 @@ function getJSF(k1, k2) {
else
u1 = m14;
}
// 将计算得到的 `u1` 值存入 `jsf` 数组对应 `k1` 的位置
jsf[0].push(u1);
var u2;
@ -113,10 +82,9 @@ function getJSF(k1, k2) {
else
u2 = m24;
}
// 将计算得到的 `u2` 值存入 `jsf` 数组对应 `k2` 的位置
jsf[1].push(u2);
// 第二阶段:根据 `u1` 和 `u2` 的值来更新 `d1` 和 `d2` 的值,并将 `k1` 和 `k2` 向右逻辑移位 1 位(除以 2为下一轮计算做准备
// Second phase
if (2 * d1 === u1 + 1)
d1 = 1 - d1;
if (2 * d2 === u2 + 1)
@ -125,38 +93,27 @@ function getJSF(k1, k2) {
k2.iushrn(1);
}
// 返回表示联合稀疏形式的二维数组 `jsf`
return jsf;
}
// 将 `getJSF` 函数挂载到 `utils` 对象上,方便外部调用
utils.getJSF = getJSF;
// 函数 `cachedProperty` 的功能是为对象的原型添加一个缓存属性,即第一次访问属性时会通过传入的计算函数 `computer` 来计算属性值并缓存起来,后续访问直接返回缓存的值
function cachedProperty(obj, name, computer) {
// 构造一个缓存属性的键名,以下划线开头加上属性名
var key = '_' + name;
// 在传入对象 `obj` 的原型上定义属性 `name`,通过闭包实现缓存逻辑
obj.prototype[name] = function cachedProperty() {
// 如果缓存属性的值已经存在(`this[key]` 不为 `undefined`),则直接返回缓存的值
return this[key]!== undefined? this[key] :
// 否则调用传入的计算函数 `computer` 计算属性值,将计算结果缓存到 `this[key]` 并返回该结果
this[key] = computer.call(this);
return this[key] !== undefined ? this[key] :
this[key] = computer.call(this);
};
}
// 将 `cachedProperty` 函数挂载到 `utils` 对象上,方便外部使用来为对象添加缓存属性
utils.cachedProperty = cachedProperty;
// 函数 `parseBytes` 的功能是根据输入参数的类型进行不同的处理,如果是字符串类型,则调用 `utils` 对象中的 `toArray` 函数(假设是将十六进制字符串转换为字节数组的功能)将其转换为字节数组形式,否则直接返回输入的字节数据
function parseBytes(bytes) {
return typeof bytes === 'string'? utils.toArray(bytes, 'hex') :
bytes;
return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') :
bytes;
}
// 将 `parseBytes` 函数挂载到 `utils` 对象上,方便外部调用进行字节数据的解析处理
utils.parseBytes = parseBytes;
// 函数 `intFromLE` 的功能是将给定的字节数据(以十六进制字符串表示或字节数组形式)转换为大整数(`BN` 类型并按照小端序Little-Endian进行解析
function intFromLE(bytes) {
return new BN(bytes, 'hex', 'le');
}
// 将 `intFromLE` 函数挂载到 `utils` 对象上,方便外部调用进行小端序字节数据到大整数的转换
utils.intFromLE = intFromLE;
utils.intFromLE = intFromLE;

@ -1,150 +1,80 @@
// 引入 `once` 模块,`once` 函数通常用于确保一个回调函数只会被调用一次,
// 在后续代码中可能用于避免多次触发相同的回调逻辑,保证事件处理的唯一性。
var once = require('once');
// 定义一个空函数 `noop`,它不执行任何实际操作,在代码中可能作为默认的回调占位符使用,
// 当需要一个空的函数回调,但又不想传入 `null` 或 `undefined` 时可以用它。
var noop = function() {};
// 定义一个函数 `isRequest`,用于判断传入的 `stream` 参数是否代表一个请求相关的流对象。
// 判断依据是流对象是否具有 `setHeader` 方法并且 `abort` 属性是一个函数,
// 通常符合这种特征的流对象可能是与 HTTP 请求等相关的可操作流。
var isRequest = function(stream) {
return stream.setHeader && typeof stream.abort === 'function';
};
// 定义一个函数 `isChildProcess`,用于判断传入的 `stream` 参数是否代表一个子进程相关的流对象。
// 判断条件是流对象具有 `stdio` 属性,且 `stdio` 是一个长度为3的数组
// 这符合 Node.js 中子进程标准输入、输出、错误流的一种常见表示形式。
var isChildProcess = function(stream) {
return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3;
return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3
};
// 定义 `eos` 函数,它主要用于处理流(`stream`)的结束相关逻辑,比如流在可读、可写结束,
// 或者出现错误、关闭等情况时执行相应的回调函数,并提供了取消监听等相关功能。
var eos = function(stream, opts, callback) {
// 如果传入的 `opts` 参数是一个函数,说明可能只传入了回调函数而省略了 `opts` 对象,
// 则将当前的 `opts` 当作 `null`,并把传入的函数当作回调函数重新调用 `eos` 函数进行处理。
if (typeof opts === 'function') return eos(stream, null, opts);
// 如果没有传入 `opts` 参数,则初始化它为空对象,确保后续使用时有默认值可参考。
if (!opts) opts = {};
// 使用 `once` 函数包装传入的回调函数(如果没有传入回调函数,则使用之前定义的 `noop` 空函数作为默认),
// 保证回调函数只会被执行一次,避免重复触发带来的问题。
callback = once(callback || noop);
// 获取流对象的可写状态对象,通常包含了流可写相关的一些属性,比如缓冲区状态等,
// 后续可根据这个状态对象判断流的可写情况以及进行相关逻辑处理。
var ws = stream._writableState;
// 获取流对象的可读状态对象,类似地,它包含了流可读相关的属性,如已读取的数据量、是否结束等信息,
// 用于判断流的可读状态及后续逻辑处理。
var rs = stream._readableState;
// 根据传入的 `opts.readable` 属性或者流本身的可读属性(如果 `opts.readable` 未明确设置为 `false`)来确定 `readable` 的值,
// 表示流是否处于可读状态,用于后续判断流结束等相关逻辑。
var readable = opts.readable || (opts.readable!== false && stream.readable);
// 按照类似的规则确定 `writable` 的值,即根据 `opts.writable` 属性或者流本身的可写属性(若 `opts.writable` 未设为 `false`)来判断流是否可写,
// 用于后续相关逻辑判断。
var writable = opts.writable || (opts.writable!== false && stream.writable);
// 定义一个变量 `cancelled`,用于标记是否取消了相关的事件监听,初始值为 `false`
// 后续在取消操作时会将其设置为 `true`。
// 定义 `onlegacyfinish` 函数,它作为一个事件处理回调函数,用于处理旧版本流(`legacy streams`)的 `end` 或 `close` 事件。
// 如果流已经不可写了,就调用 `onfinish` 函数,进行后续的结束相关处理。
var readable = opts.readable || (opts.readable !== false && stream.readable);
var writable = opts.writable || (opts.writable !== false && stream.writable);
var cancelled = false;
var onlegacyfinish = function() {
if (!stream.writable) onfinish();
};
// 定义 `onfinish` 函数,用于处理流可写部分结束的情况。
// 首先将 `writable` 设置为 `false`,表示可写状态结束,然后如果流已经不可读了,就调用传入的 `callback` 回调函数,
// 传递 `stream` 作为 `this` 上下文,触发相应的业务逻辑(比如通知外部流的可写部分已完成等)。
var onfinish = function() {
writable = false;
if (!readable) callback.call(stream);
};
// 定义 `onend` 函数,用于处理流可读部分结束的情况。
// 将 `readable` 设置为 `false`,表示可读状态结束,接着如果流已经不可写了,就调用 `callback` 回调函数,
// 同样传递 `stream` 作为 `this` 上下文,执行相应的逻辑(例如通知外部流的可读部分已完成等)。
var onend = function() {
readable = false;
if (!writable) callback.call(stream);
};
// 定义 `onexit` 函数,用于处理子进程流(通过 `isChildProcess` 判断)退出的情况。
// 根据子进程的退出码(`exitCode`)来决定回调函数 `callback` 的参数,如果退出码存在(表示有错误),
// 则传入一个带有错误信息的 `Error` 对象作为回调函数的参数,否则传入 `null`,以此来通知外部子进程的退出状态。
var onexit = function(exitCode) {
callback.call(stream, exitCode? new Error('exited with error code: ' + exitCode) : null);
callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null);
};
// 定义 `onerror` 函数,用于处理流发生错误的情况,直接将错误对象 `err` 作为参数传递给 `callback` 回调函数,
// 使得外部可以知晓流出现了错误并进行相应的处理(比如记录日志、进行错误提示等)。
var onerror = function(err) {
callback.call(stream, err);
};
// 定义 `onclose` 函数,用于处理流关闭的情况。
// 它会在下一个事件循环(`process.nextTick`)中调用 `onclosenexttick` 函数,这样做可以确保一些异步操作的顺序和时机控制,
// 避免在关闭事件触发时立即执行可能还未准备好的逻辑。
var onclose = function() {
process.nextTick(onclosenexttick);
};
// 定义 `onclosenexttick` 函数,用于在流关闭后的下一个事件循环中进行更细致的判断和处理。
// 如果已经取消了相关操作(`cancelled` 为 `true`),则直接返回,不进行后续处理。
// 如果流处于可读状态且可读状态对象存在,并且可读状态不是正常结束(`rs.ended` 为 `false` 或者 `rs.destroyed` 为 `true`
// 则调用 `callback` 回调函数并传入一个表示过早关闭的 `Error` 对象,提示外部流关闭不符合预期(可读部分还未正常结束)。
// 同理,如果流处于可写状态且可写状态对象存在,并且可写状态不是正常结束(`ws.ended` 为 `false` 或者 `ws.destroyed` 为 `true`
// 也调用 `callback` 回调函数并传入相应的错误对象,提示可写部分过早关闭。
var onclosenexttick = function() {
if (cancelled) return;
if (readable &&!(rs && (rs.ended &&!rs.destroyed))) return callback.call(stream, new Error('premature close'));
if (writable &&!(ws && (ws.ended &&!ws.destroyed))) return callback.call(stream, new Error('premature close'));
if (readable && !(rs && (rs.ended && !rs.destroyed))) return callback.call(stream, new Error('premature close'));
if (writable && !(ws && (ws.ended && !ws.destroyed))) return callback.call(stream, new Error('premature close'));
};
// 定义 `onrequest` 函数,用于在请求相关流(通过 `isRequest` 判断)的请求对象(`stream.req`)上监听 `finish` 事件,
// 当请求完成时调用 `onfinish` 函数进行后续处理。
var onrequest = function() {
stream.req.on('finish', onfinish);
};
// 根据流对象的类型进行不同的事件监听设置。
// 如果流对象是请求相关的流(通过 `isRequest` 判断为真),则进行以下事件监听:
// 监听 `complete` 事件,当请求完成时调用 `onfinish` 函数;
// 监听 `abort` 事件,当请求被中止时调用 `onclose` 函数;
// 如果流对象有请求对象(`stream.req`),则调用 `onrequest` 函数进行 `finish` 事件监听,
// 否则监听 `request` 事件,当有请求时调用 `onrequest` 函数进行后续处理。
if (isRequest(stream)) {
stream.on('complete', onfinish);
stream.on('abort', onclose);
if (stream.req) onrequest();
else stream.on('request', onrequest);
} else if (writable &&!ws) { // legacy streams
// 如果流对象是旧版本的可写流(可写但没有可写状态对象,通过 `writable &&!ws` 判断),
// 则监听 `end` 和 `close` 事件,当这些事件触发时调用 `onlegacyfinish` 函数进行处理。
} else if (writable && !ws) { // legacy streams
stream.on('end', onlegacyfinish);
stream.on('close', onlegacyfinish);
}
// 如果流对象是子进程相关的流(通过 `isChildProcess` 判断为真),则监听 `exit` 事件,
// 当子进程退出时调用 `onexit` 函数进行相应的处理,通知外部子进程的退出状态。
if (isChildProcess(stream)) stream.on('exit', onexit);
// 无论流对象属于哪种类型,都统一监听以下一些通用的事件:
// 监听 `end` 事件,当可读部分结束时调用 `onend` 函数进行处理;
// 监听 `finish` 事件,当可写部分结束时调用 `onfinish` 函数进行处理;
// 如果 `opts.error` 没有明确设置为 `false`(默认情况下),则监听 `error` 事件,
// 当流出现错误时调用 `onerror` 函数传递错误信息给回调函数;
// 监听 `close` 事件,当流关闭时调用 `onclose` 函数进行后续处理。
stream.on('end', onend);
stream.on('finish', onfinish);
if (opts.error!== false) stream.on('error', onerror);
if (opts.error !== false) stream.on('error', onerror);
stream.on('close', onclose);
// 返回一个函数,这个函数用于取消之前对各种事件的监听操作。
// 当调用这个返回的函数时,会将 `cancelled` 标记设置为 `true`,表示已取消监听,
// 然后依次移除之前添加的各个事件监听器,避免后续不必要的事件触发和逻辑执行,
// 比如移除对 `complete`、`abort`、`request`、`finish`、`exit`、`end`、`error`、`close` 等事件的监听器。
return function() {
cancelled = true;
stream.removeListener('complete', onfinish);
@ -161,5 +91,4 @@ var eos = function(stream, opts, callback) {
};
};
// 将 `eos` 函数作为模块的导出内容,使得其他模块可以通过引入该模块来使用 `eos` 函数处理流的结束相关逻辑。
module.exports = eos;
module.exports = eos;

@ -1,110 +1,57 @@
// 启用严格模式在严格模式下JavaScript 代码会遵循更严格的语法和行为规范,有助于发现更多潜在的错误并提升代码质量。
"use strict";
// 引入 `es5-ext/array/#/clear` 模块,可能用于清空数组相关操作,从模块路径推测它与扩展的数组功能相关。
// 这里将引入的模块赋值给 `clear` 变量,以便后续在代码中使用对应的功能。
var clear = require("es5-ext/array/#/clear")
// 引入 `es5-ext/object/assign` 模块,通常用于将多个对象的属性合并到一个目标对象上,类似 `Object.assign` 的功能,
// 但可能是经过扩展或具有特定兼容性处理的版本,赋值给 `assign` 变量供后续使用。
, assign = require("es5-ext/object/assign")
// 引入 `es5-ext/object/valid-callable` 模块,可能用于验证某个值是否为可调用的(比如函数),保障后续代码中对可调用对象的使用安全,
// 并将其赋值给 `callable` 变量。
, callable = require("es5-ext/object/valid-callable")
// 引入 `es5-ext/object/valid-value` 模块,大概是用于验证某个值是否符合特定的有效性要求,具体取决于该模块内部实现,
// 赋值为 `value` 变量供代码中进行值的有效性判断使用。
, value = require("es5-ext/object/valid-value")
// 引入 `d` 模块,从代码使用情况看,它可能是一个用于创建具有特定属性描述符的对象属性的工具函数(类似 `Object.defineProperty` 的简化或扩展用法),
// 具体功能需看其内部实现细节,赋值给 `d` 变量以便后续使用。
, d = require("d")
// 引入 `d/auto-bind` 模块,推测是用于自动绑定函数上下文(比如将函数的 `this` 指向特定对象)的功能模块,
// 赋值给 `autoBind` 变量用于后续相关操作。
, autoBind = require("d/auto-bind")
// 引入 `es6-symbol` 模块,用于在 JavaScript 中支持 `Symbol` 类型,`Symbol` 常用于创建唯一的、不可变的标识符,
// 在这里用于定义一些特殊的属性键等情况,赋值给 `Symbol` 变量供代码使用。
, Symbol = require("es6-symbol");
, assign = require("es5-ext/object/assign")
, callable = require("es5-ext/object/valid-callable")
, value = require("es5-ext/object/valid-value")
, d = require("d")
, autoBind = require("d/auto-bind")
, Symbol = require("es6-symbol");
// 获取 `Object.defineProperty` 和 `Object.defineProperties` 方法的引用,分别用于定义单个对象属性和多个对象属性,
// 后续会使用它们来创建具有特定行为和属性描述符的对象属性,确保对象属性的正确设置和访问控制等。
var defineProperty = Object.defineProperty, defineProperties = Object.defineProperties, Iterator;
// 将 `Iterator` 函数作为模块的导出内容,同时在当前模块内也使用 `Iterator` 变量来指代这个函数,
// 该函数用于创建一个迭代器对象,用于遍历某个列表(`list`)并处理相关的更新操作(如添加、删除、清空元素等情况)。
module.exports = Iterator = function (list, context) {
// 检查是否使用 `new` 关键字来调用 `Iterator` 构造函数,如果不是,则抛出一个 `TypeError` 异常,
// 这是遵循构造函数的常规使用规范,确保对象实例化的正确方式。
if (!(this instanceof Iterator)) throw new TypeError("Constructor requires 'new'");
// 使用 `Object.defineProperties` 方法为当前实例(`this`)定义三个属性:`__list__`、`__context__` 和 `__nextIndex__`。
// 通过 `d` 函数(具体功能由其模块实现决定)来设置属性的描述符,使其具有特定的可写性(`"w"` 表示可写)等特征。
// `__list__` 属性存储传入的要迭代的列表数据(通过 `value` 函数验证其有效性后赋值),
// `__context__` 属性存储传入的上下文对象(如果有的话),`__nextIndex__` 属性初始化为 `0`,用于记录迭代的位置索引。
defineProperties(this, {
__list__: d("w", value(list)),
__context__: d("w", context),
__nextIndex__: d("w", 0)
});
// 如果没有传入上下文对象(`context` 为 `null` 或 `undefined`),则直接返回,不进行后续与上下文相关的操作(比如监听上下文事件等)。
if (!context) return;
// 使用 `callable` 函数验证上下文对象的 `on` 方法是否是可调用的(即是否为函数),确保后续可以安全地进行事件监听绑定操作。
callable(context.on);
// 在上下文对象上监听 `_add` 事件,当该事件触发时,调用当前迭代器实例的 `_onAdd` 方法进行处理,
// 这样迭代器可以根据列表元素添加的情况更新自身的迭代状态等信息。
context.on("_add", this._onAdd);
// 类似地,监听 `_delete` 事件,触发时调用 `_onDelete` 方法,用于处理列表元素删除时迭代器的相应调整。
context.on("_delete", this._onDelete);
// 监听 `_clear` 事件,触发时调用 `_onClear` 方法,以应对列表被清空的情况对迭代器状态进行处理。
context.on("_clear", this._onClear);
};
// 删除 `Iterator` 函数原型对象上的 `constructor` 属性,这样在使用迭代器实例时,不会默认暴露其构造函数引用,
// 可能是出于封装或避免不必要的外部访问构造函数的考虑。
// Internal %IteratorPrototype% doesn't expose its constructor
delete Iterator.prototype.constructor;
// 使用 `Object.defineProperties` 方法为 `Iterator` 函数的原型对象(`Iterator.prototype`)定义多个属性和方法,
// 通过 `assign` 函数将多个属性描述对象合并后进行定义,实现了迭代器的核心功能以及相关的辅助方法。
defineProperties(
Iterator.prototype,
assign(
{
// `_next` 方法是迭代器内部用于获取下一个元素索引的私有方法(以下划线开头通常表示私有方法,虽然 JavaScript 并没有真正的私有方法概念,但这是一种约定)。
// 它的逻辑如下:
// 如果 `__list__` 属性不存在(可能表示列表已经被清空或者未正确初始化等情况),则返回 `undefined`,表示迭代结束。
// 如果存在 `__redo__` 属性(这个属性可能用于记录需要重新处理的索引情况,具体看其使用场景),则从 `__redo__` 数组中取出第一个元素作为索引 `i`
// 如果取出的元素不为 `undefined`,则返回该索引值,表示下一个要处理的元素索引;否则继续后续判断。
// 如果当前的 `__nextIndex__` 小于列表的长度(即还有未迭代的元素),则返回当前 `__nextIndex__` 的值,并将其自增 `1`,用于指向下一个待迭代元素的索引。
// 如果以上条件都不满足,说明迭代已经完成,调用 `_unBind` 方法进行一些清理和解除绑定操作,然后返回 `undefined`,表示迭代结束。
_next: d(function () {
var i;
if (!this.__list__) return undefined;
if (this.__redo__) {
i = this.__redo__.shift();
if (i!== undefined) return i;
if (i !== undefined) return i;
}
if (this.__nextIndex__ < this.__list__.length) return this.__nextIndex__++;
this._unBind();
return undefined;
}),
// `next` 方法是符合迭代器协议的公开方法,用于获取下一个迭代元素的结果对象。
// 它调用 `_next` 方法获取下一个元素的索引,然后将索引传递给 `_createResult` 方法来创建并返回包含 `done`(表示是否迭代完成)和 `value`(迭代元素的值)属性的结果对象。
next: d(function () {
return this._createResult(this._next());
}),
// `_createResult` 方法用于根据传入的索引值创建迭代结果对象。
// 如果索引值为 `undefined`,表示迭代已经完成,返回一个 `{ done: true, value: undefined }` 的对象,符合迭代器协议中迭代结束的表示形式。
// 如果索引值不为 `undefined`,则表示还有元素可迭代,通过调用 `_resolve` 方法获取对应索引位置的元素值,并返回一个 `{ done: false, value: 元素值 }` 的对象,
// 表示迭代未结束且包含当前迭代元素的值。
_createResult: d(function (i) {
if (i === undefined) return { done: true, value: undefined };
return { done: false, value: this._resolve(i) };
}),
// `_resolve` 方法用于根据索引值从 `__list__` 列表中获取对应的元素,简单地返回 `__list__` 数组中指定索引位置的元素,
// 是获取迭代元素实际值的一个辅助方法。
_resolve: d(function (i) {
return this.__list__[i];
}),
// `_unBind` 方法用于解除迭代器与相关对象的绑定关系以及进行一些清理操作。
// 首先将 `__list__` 属性设置为 `null`,表示不再关联列表数据,然后删除 `__redo__` 属性(如果存在)。
// 如果存在 `__context__` 属性(即有绑定的上下文对象),则在上下文对象上移除对 `_add`、`_delete`、`_clear` 事件的监听(通过 `off` 方法,与之前的 `on` 方法对应),
// 最后将 `__context__` 属性也设置为 `null`,彻底解除与上下文的关联,完成清理和解除绑定的过程。
_unBind: d(function () {
this.__list__ = null;
delete this.__redo__;
@ -114,22 +61,11 @@ defineProperties(
this.__context__.off("_clear", this._onClear);
this.__context__ = null;
}),
// `toString` 方法用于返回迭代器对象的字符串表示形式,遵循 `[object 类型名称]` 的格式,
// 如果对象定义了 `Symbol.toStringTag` 属性,则使用该属性的值作为类型名称,否则使用 `Object` 作为默认类型名称。
toString: d(function () {
return "[object " + (this[Symbol.toStringTag] || "Object") + "]";
})
},
// 使用 `autoBind` 函数(来自 `d/auto-bind` 模块)对 `_onAdd`、`_onDelete`、`_onClear` 这几个方法进行自动绑定操作,
// 使得这些方法内部的 `this` 指针始终指向当前迭代器实例,方便在事件触发等场景下正确访问和操作迭代器的属性和方法。
autoBind({
// `_onAdd` 方法用于处理列表中元素添加的情况,它接收添加元素的索引 `index` 作为参数,逻辑如下:
// 如果添加元素的索引大于等于当前迭代器的 `__nextIndex__`(表示添加的元素在当前迭代位置之后,不会影响当前迭代过程),则直接返回,不做处理。
// 如果添加元素的索引小于 `__nextIndex__`,说明添加的元素影响了当前的迭代顺序,需要调整迭代器的状态,将 `__nextIndex__` 自增 `1`,表示下一个迭代元素的索引往后移一位。
// 如果不存在 `__redo__` 属性(可能之前没有需要重新处理的索引情况),则创建一个新的 `__redo__` 属性,它是一个数组,并将添加元素的索引 `index` 放入数组中,
// 这样后续迭代时可以根据这个数组来重新处理添加元素后的情况。
// 如果已经存在 `__redo__` 属性,则遍历 `__redo__` 数组,对于每个元素 `redo`,如果它大于等于添加元素的索引 `index`,则将其自增 `1`
// 表示这些需要重新处理的索引位置也需要往后移一位,以适应新添加的元素,最后将添加元素的索引 `index` 也添加到 `__redo__` 数组末尾。
_onAdd: d(function (index) {
if (index >= this.__nextIndex__) return;
++this.__nextIndex__;
@ -142,27 +78,17 @@ defineProperties(
}, this);
this.__redo__.push(index);
}),
// `_onDelete` 方法用于处理列表中元素删除的情况,接收要删除元素的索引 `index` 作为参数,逻辑如下:
// 如果要删除元素的索引大于等于当前迭代器的 `__nextIndex__`(表示删除的元素在当前迭代位置之后,不影响当前迭代过程),则直接返回,不做处理。
// 如果要删除元素的索引小于 `__nextIndex__`,说明删除的元素影响了当前的迭代顺序,需要调整迭代器的状态,将 `__nextIndex__` 自减 `1`,表示下一个迭代元素的索引往前移一位。
// 如果不存在 `__redo__` 属性(即之前没有需要重新处理的索引情况),则直接返回,因为没有需要调整的额外索引记录。
// 如果存在 `__redo__` 属性,则先查找要删除元素的索引 `index` 在 `__redo__` 数组中的位置 `i`,如果找到(`i` 不为 `-1`),则从数组中删除该元素(通过 `splice` 方法)。
// 接着遍历 `__redo__` 数组,对于每个元素 `redo`,如果它大于要删除元素的索引 `index`,则将其自减 `1`
// 表示这些需要重新处理的索引位置需要往前移一位,以适应元素被删除后的情况。
_onDelete: d(function (index) {
var i;
if (index >= this.__nextIndex__) return;
--this.__nextIndex__;
if (!this.__redo__) return;
i = this.__redo__.indexOf(index);
if (i!== -1) this.__redo__.splice(i, 1);
if (i !== -1) this.__redo__.splice(i, 1);
this.__redo__.forEach(function (redo, j) {
if (redo > index) this.__redo__[j] = --redo;
}, this);
}),
// `_onClear` 方法用于处理列表被清空的情况,逻辑如下:
// 如果存在 `__redo__` 属性(表示之前有需要重新处理的索引情况),则调用 `clear` 函数(来自 `es5-ext/array/#/clear` 模块,用于清空数组)清空 `__redo__` 数组。
// 最后将 `__nextIndex__` 属性重置为 `0`,表示迭代器回到初始状态,等待新的列表数据进行迭代。
_onClear: d(function () {
if (this.__redo__) clear.call(this.__redo__);
this.__nextIndex__ = 0;
@ -171,8 +97,6 @@ defineProperties(
)
);
// 使用 `Object.defineProperty` 方法在 `Iterator` 函数的原型对象上定义一个 `Symbol.iterator` 属性,
// 其值是一个函数,该函数直接返回当前迭代器对象本身,这符合迭代器协议的要求,使得该迭代器对象可以被用于 `for...of` 循环等支持迭代器的语法结构中。
defineProperty(
Iterator.prototype,
Symbol.iterator,

219
node_modules/oboe/Gruntfile.js generated vendored

@ -1,9 +1,5 @@
// 将一个函数作为模块的导出内容,这个函数接收 `grunt` 对象作为参数,`grunt` 是 Grunt 构建工具的核心对象,
// 通常用于配置任务、加载插件以及执行各种构建相关的操作,在这里的函数内部会基于 `grunt` 进行一系列任务的配置和定义。
module.exports = function (grunt) {
// 定义 `runNpmScript` 函数,用于运行 `npm` 脚本命令,它接收要执行的 `npm` 脚本命令名称(`command`)以及一个回调函数(`cb`)作为参数。
function runNpmScript (command, cb) {
// 配置执行 `npm` 脚本的相关选项,创建一个包含 `cmd`(要执行的命令,这里固定为 `npm`)、`args`(传递给 `npm` 命令的参数数组,这里包含 `run` 和具体的脚本命令名称)以及 `opts`(执行命令的其他选项,这里设置 `stdio` 为 `inherit`,表示继承父进程的标准输入、输出和错误输出流,使得执行过程中的信息能直接在终端显示)的对象 `opts`。
var opts = {
cmd: 'npm',
args: ['run', command],
@ -12,26 +8,18 @@ module.exports = function (grunt) {
}
}
// 使用 `grunt.util.spawn` 方法来启动一个子进程执行配置好的 `npm` 命令,该方法会异步执行命令并在执行完成后触发回调函数,回调函数接收 `error`(执行过程中出现的错误对象,如果没有错误则为 `null`)、`result`(命令执行的结果信息,具体内容根据命令而定)以及 `code`(命令的退出状态码)作为参数。
grunt.util.spawn(opts, function (error, result, code) {
// 如果执行过程中出现错误(即 `error` 不为 `null`),则使用 `grunt.fail.warn` 方法向终端输出警告信息,提示指定的 `npm` 脚本命令执行失败,并显示命令名称。
if (error) {
grunt.fail.warn(command +'failed.')
grunt.fail.warn(command + ' failed.')
}
// 无论命令执行是否成功,都执行传入的回调函数 `cb`,以便外部在调用 `runNpmScript` 函数后能根据需要进行后续的操作,比如继续执行其他任务等。
cb()
})
}
// 定义一个数组,包含了在某些任务执行时默认自动启动的浏览器名称列表,这里列出了 `Chrome`、`Firefox` 和 `Safari`
// 用于后续配置如测试任务等在哪些浏览器中运行的相关设置。
var autoStartBrowsers = ['Chrome', 'Firefox', 'Safari']
// 定义一个常量,表示 HTTP 协议下的流数据源端口号,用于在相关任务(如测试等)中与服务器进行通信等操作时指定端口,这里设置为 `4567`。
var STREAM_SOURCE_PORT_HTTP = 4567
// 定义一个数组,包含了触发 Karma 测试任务的文件路径匹配模式,这些文件的变更通常会触发相应的测试任务重新执行,
// 例如 `src` 目录下所有子目录中的 `.js` 文件、`test/specs` 目录下的 `.spec.js` 文件等,方便在监听文件变化并自动运行测试的场景下确定哪些文件的变化需要关注。
var FILES_TRIGGERING_KARMA = [
'src/**/*.js',
'test/specs/*.spec.js',
@ -39,22 +27,13 @@ module.exports = function (grunt) {
'test/*.js'
]
// 使用 `grunt.initConfig` 方法来初始化 Grunt 的配置对象,在这个对象中可以定义各种任务的配置参数、文件路径等信息,
// 后续加载的 Grunt 插件以及自定义的任务都会根据这里的配置来执行相应的操作。
grunt.initConfig({
// 读取项目根目录下 `package.json` 文件的内容,并将其解析为 JSON 对象后赋值给 `pkg` 属性,这样在后续配置中可以方便地引用项目的元信息,比如项目名称、版本号等,例如在生成文件名时可以包含版本号等信息。
pkg: grunt.file.readJSON('package.json'),
// 配置 `clean` 任务的参数,这里指定要清理的文件路径模式,即 `dist` 目录下所有的 `.js` 文件以及 `build` 目录下所有的 `.js` 文件,
// 通常用于在构建之前清理之前生成的旧文件,保证构建环境的干净。
clean: ['dist/*.js', 'build/*.js'],
// 配置 `karma` 任务的相关参数,`karma` 通常用于在不同浏览器中运行 JavaScript 测试用例,这里定义了多个不同的配置选项和子任务配置。
karma: {
// 通用的 `karma` 任务选项,适用于所有 `karma` 子任务(除非子任务中单独覆盖这些配置),例如设置 `singleRun` 为 `true` 表示只运行一次测试然后退出,
// 设置 `proxies` 用于配置代理,将 `/testServer` 代理到 `http://localhost:` + `STREAM_SOURCE_PORT_HTTP`,方便在测试中访问特定的服务器资源,
// 配置 `reporters` 为 `['progress']` 表示使用进度条形式的测试结果报告器,`colors` 为 `true` 表示在输出的报告和日志中启用颜色显示,使输出更直观易读。
options: {
singleRun: true,
proxies: {
@ -68,9 +47,6 @@ module.exports = function (grunt) {
colors: true
},
// `coverage` 子任务配置,用于生成测试覆盖率报告,设置 `reporters` 为 `['coverage']` 表示使用覆盖率报告器,
// 配置 `preprocessors` 来指定哪些源文件需要生成覆盖率信息(这里是 `src` 目录下所有子目录中的 `.js` 文件,这些文件会被 Istanbul 工具进行插桩处理以收集覆盖率数据),
// 指定 `browsers` 为 `['PhantomJS']` 表示在 PhantomJS 浏览器中运行测试,`configFile` 为 `test/unit.conf.js` 表示使用该配置文件来进一步配置 Karma 任务的详细参数。
'coverage': {
reporters: ['coverage'],
preprocessors: {
@ -82,9 +58,6 @@ module.exports = function (grunt) {
configFile: 'test/unit.conf.js'
},
// `precaptured-dev` 子任务配置,用于在开发过程中针对已经捕获的浏览器(可能是之前手动启动或者通过其他方式准备好的浏览器环境)进行单次测试运行,
// 适用于在一些特殊情况下(如在 Unix 开发环境中要在 Windows VM 里运行的 IE 浏览器等 Karma 不容易直接启动的浏览器环境)运行测试,这里 `browsers` 设为空数组表示使用已捕获的浏览器,
// `configFile` 为 `test/unit.conf.js``singleRun` 设为 `true` 表示单次运行测试。
'precaptured-dev': {
// for doing a single test run with already captured browsers during development.
// this is good for running tests in browsers karma can't easily start such as
@ -94,44 +67,31 @@ module.exports = function (grunt) {
singleRun: 'true'
},
// `single-dev` 子任务配置,用于在开发过程中在默认自动启动的浏览器(前面定义的 `autoStartBrowsers` 数组中的浏览器)中运行测试,
// `configFile` 为 `test/unit.conf.js`,表示使用该配置文件来配置详细的测试参数。
'single-dev': {
browsers: autoStartBrowsers,
configFile: 'test/unit.conf.js'
},
// `single-concat` 子任务配置,类似 `single-dev`,也是在默认自动启动的浏览器中运行测试,但使用 `test/concat.conf.js` 作为配置文件,
// 可能在这个配置文件中定义了与文件合并concat相关的测试特定设置具体配置取决于该文件内容。
'single-concat': {
browsers: autoStartBrowsers,
configFile: 'test/concat.conf.js'
},
// `single-minified` 子任务配置,同样在默认自动启动的浏览器中运行测试,不过使用 `test/min.conf.js` 作为配置文件,
// 可能该配置文件针对测试已经压缩minified后的代码进行了相应的配置用于验证压缩后代码的测试情况等。
'single-minified': {
browsers: autoStartBrowsers,
configFile: 'test/min.conf.js'
},
// `single-amd` 子任务配置,在默认自动启动的浏览器中运行测试,使用 `test/amd.conf.js` 作为配置文件,
// 也许这个配置文件是针对使用 AMDAsynchronous Module Definition异步模块定义规范的代码进行测试相关的设置用于测试遵循 AMD 规范的模块情况。
'single-amd': {
browsers: autoStartBrowsers,
configFile: 'test/amd.conf.js'
},
// `single-browser-http` 子任务配置,在默认自动启动的浏览器中运行测试,使用 `test/http.conf.js` 作为配置文件,
// 推测该配置文件与基于 HTTP 协议相关的测试场景或者配置有联系,用于特定的 HTTP 相关的测试需求。
'single-browser-http': {
browsers: autoStartBrowsers,
configFile: 'test/http.conf.js'
},
// `persist` 子任务配置,用于设置一个持久化的 Karma 服务器,`singleRun` 设为 `false` 表示服务器启动后不会自动运行一次测试就关闭,而是持续运行等待后续指令,
// `background` 设为 `true` 表示在后台运行服务器,`browsers` 设为空数组可能表示初始启动时不指定具体浏览器,后续可以通过其他方式添加浏览器来运行测试,
// `configFile` 为 `test/unit.conf.js`,表示使用该配置文件来配置服务器的基本参数,通过 `karma:persist` 任务可以启动这个服务器,通过 `karma:persist:run` 任务可以在已启动的服务器上运行测试。
'persist': {
// for setting up a persistent karma server.
// To start the server, the task is:
@ -144,56 +104,39 @@ module.exports = function (grunt) {
background: true
},
// `single-heap` 子任务配置,使用 `test/heap.conf.js` 作为配置文件来运行测试具体配置取决于该配置文件内容可能与内存堆heap相关的测试场景或者设置有关系。
'single-heap': {
configFile: 'test/heap.conf.js'
}
},
// 配置 `exec` 任务的相关参数,`exec` 任务通常用于在 Grunt 构建过程中执行外部命令,这里定义了多个不同的命令配置,每个配置对应一个子任务。
exec: {
// `reportMinifiedSize` 子任务配置,执行一个命令用于报告压缩后的文件大小,通过 `echo` 命令输出 `minified size is` 以及使用 `wc -c` 命令统计 `dist/oboe-browser.min.js` 文件的字节数,
// 这里的命令在 Windows 系统下可能不太适用(注释中也提到了,建议使用 Cygwin因为 `wc` 等命令是类 Unix 系统下常用的文本处理命令。
// these might not go too well on Windows :-) - get Cygwin.
reportMinifiedSize: {
command: 'echo minified size is `wc -c < dist/oboe-browser.min.js` bytes'
},
// `reportMinifiedAndGzippedSize` 子任务配置,执行一个命令用于报告压缩并经过 `gzip` 处理后的文件大小,通过 `echo` 命令输出 `Size after gzip is` 以及使用 `gzip --best --stdout` 对 `dist/oboe-browser.min.js` 文件进行最佳压缩并输出到标准输出,
// 再通过 `wc -c` 命令统计输出内容的字节数,最后显示字节数并提示最大值为 `5120`,同样这个命令在 Windows 系统下可能需要特殊处理(如借助 Cygwin 等工具)才能正确执行。
reportMinifiedAndGzippedSize: {
command: 'echo Size after gzip is `gzip --best --stdout dist/oboe-browser.min.js | wc -c` bytes - max 5120'
},
// `createGitVersionJs` 子任务配置,执行一个命令用于创建一个包含 Git 版本描述信息的文件,通过 `echo` 命令将 `git describe` 执行的结果(获取当前 Git 仓库的版本描述信息)输出到 `build/version.txt` 文件中,用于记录项目的版本相关信息。
createGitVersionJs: {
command: 'echo "`git describe`" > build/version.txt'
},
// `webpackBrowser` 子任务配置,执行 `npm run webpack` 命令,通常用于启动 Webpack 构建任务来处理浏览器相关的代码,比如打包、编译等操作,具体 Webpack 的配置取决于项目中 `webpack` 脚本对应的配置。
webpackBrowser: {
command: 'npm run webpack'
},
// `webpackNode` 子任务配置,执行 `npm run webpack -- --config webpack.config.node.js` 命令,同样是启动 Webpack 构建任务,但使用 `webpack.config.node.js` 作为 Webpack 的配置文件,
// 用于处理 Node.js 相关代码的打包、编译等操作,与处理浏览器代码的 Webpack 配置可能有所不同,例如在模块加载、目标环境等方面会有差异。
webpackNode: {
command: 'npm run webpack -- --config webpack.config.node.js'
},
// `standard` 子任务配置,执行 `npm run standard -- --fix --envs jasmine` 命令,可能是用于运行代码规范检查工具(如 ESLint 等,这里猜测是 `standard` 相关的工具),
// 并尝试自动修复代码规范问题(通过 `--fix` 参数),同时设置特定的环境(`--envs jasmine`,可能是针对 Jasmine 测试框架相关的代码环境进行规范检查)。
standard: {
command: 'npm run standard -- --fix --envs jasmine'
}
},
// 配置 `watch` 任务的相关参数,`watch` 任务用于监听文件的变化,当指定的文件发生变化时自动触发相应的任务执行,这里定义了多个不同的监听配置,每个配置对应一个子任务。
watch: {
// `karma` 子任务配置,监听触发 Karma 测试任务的文件(前面定义的 `FILES_TRIGGERING_KARMA` 中的文件),当这些文件有变化时,执行 `karma:persist:run` 任务,
// 即运行在持久化 Karma 服务器上的测试任务,实现自动重新运行测试的功能,方便在开发过程中实时查看代码修改对测试结果的影响。
karma: {
files: FILES_TRIGGERING_KARMA,
tasks: ['karma:persist:run']
},
// `karmaAndSize` 子任务配置,类似 `watch:karma`,也是监听触发 Karma 测试任务的文件,当文件变化时,执行多个任务,包括 `karma:persist:run`(运行测试)、`browser-build`(推测是与浏览器相关的构建任务,具体内容需看对应任务定义)以及 `dist-sizes`(可能是用于查看生成文件大小相关的任务,具体要看对应任务配置),
// 这样在开发过程中不仅能自动运行测试,还能查看构建文件大小情况,确保文件大小不会超出预期等。
// like above but reports the file size. This is good for
// watching while developing to make sure it doesn't get
// too big. Doesn't run tests against minified.
@ -205,157 +148,101 @@ module.exports = function (grunt) {
'dist-sizes']
},
// `testNode` 子任务配置,监听触发 Karma 测试任务的文件,当文件变化时,执行 `node-build` 任务(推测是与 Node.js 相关的构建任务,具体要看对应任务定义),
// 用于在开发 Node.js 相关代码时,自动触发相应的构建操作,方便及时查看代码修改后的构建效果。
// like above but reports the file size. This is good for
// watching while developing to
// like above but reports the file size. This is good for
// watching while developing to make sure it doesn't get
// too big. Doesn't run tests against minified.
// `testNode` 子任务配置,用于 `watch` 任务中,监听那些会触发 `Karma` 测试任务的文件(前面已定义 `FILES_TRIGGERING_KARMA` 数组中的文件),
// 当这些文件发生变化时,会触发执行 `node-build` 任务,该任务可能是针对 Node.js 相关代码进行构建相关操作的任务,具体操作取决于 `node-build` 任务的定义。
testNode: {
files: FILES_TRIGGERING_KARMA,
tasks: [
'node-build'
]
'node-build']
},
// `restartStreamSourceAndRunTests` 子任务配置,同样用于 `watch` 任务里。它监听 `test/streamsource.js` 文件的变化,
// 当该文件改变时,会依次执行 `start-stream-source` 和 `karma:persist:run` 这两个任务。不过当前存在一个问题(注释中提到),
// 即 `start-stream-source` 任务如果多次运行会失败,因为其占用的端口会被占用(可能每次启动都尝试使用同一个固定端口导致冲突)。
restartStreamSourceAndRunTests: {
// 此处注释说明了当前存在的问题,即 `start-stream-source` 任务多次运行会失败,原因是端口被占用。
// this fails at the moment because start-stream-source
// fails if run more than once - the port is taken.
files: ['test/streamsource.js'],
tasks: ['start-stream-source', 'karma:persist:run']
},
// `standard` 子任务配置,用于 `watch` 任务里,监听那些触发 `Karma` 测试任务的文件(`FILES_TRIGGERING_KARMA` 数组中的文件)变化,
// 一旦这些文件有变动,就会执行 `exec:standard` 任务,这个任务大概率是运行代码规范检查相关的操作(前面 `exec` 任务配置中有相关定义),比如检查代码是否符合特定的代码规范标准,并可能进行自动修复等操作。
standard: {
files: FILES_TRIGGERING_KARMA,
tasks: ['exec:standard']
},
}
},
// `concurrent` 配置部分,主要用于定义可以同时执行的多个任务组合,这里定义了 `watchDev` 任务配置。
concurrent: {
// `watchDev` 任务配置,用于同时运行多个 `watch` 子任务,具体是 `watch:karmaAndSize` 和 `watch:restartStreamSourceAndRunTests` 这两个任务,
// 通过设置 `logConcurrentOutput` 为 `true`,可以让这些同时执行的任务的输出信息都能正常显示出来,方便查看每个任务的执行情况,
// 常用于在开发环境下同时监听多个不同类型的文件变化并执行相应的关联任务,提高开发效率。
watchDev: {
tasks: [ 'watch:karmaAndSize', 'watch:restartStreamSourceAndRunTests' ],
options: {
logConcurrentOutput: true
}
concurrent: {
watchDev: {
tasks: [ 'watch:karmaAndSize', 'watch:restartStreamSourceAndRunTests' ],
options: {
logConcurrentOutput: true
}
}
}
// 初始化 Grunt 配置对象结束的括号,至此完成了对 Grunt 构建任务中如 `clean`、`karma`、`exec`、`watch`、`concurrent` 等多个任务相关配置参数的定义。
})
})
// 使用 `require('matchdep').filterDev('grunt-*')` 来筛选出开发环境下所有以 `grunt-` 开头的插件,然后通过 `forEach` 方法遍历并调用 `grunt.loadNpmTasks` 函数加载这些插件,
// 使得这些插件定义的任务可以在 Grunt 构建流程中被使用,方便扩展 Grunt 的功能,添加各种自定义的构建、测试等相关任务。
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks)
// 定义一个变量 `streamSource`,用于后续管理流数据源相关的操作,初始时它未被赋值,具体的赋值和使用在相关任务中进行。
var streamSource;
var streamSource
// 注册一个名为 `start-stream-source` 的 Grunt 任务,这个任务的功能是启动一个流数据源服务,在任务函数内部实现了相关的逻辑来确保服务正确启动。
grunt.registerTask('start-stream-source', function () {
// 使用 `grunt.log.ok` 方法向控制台输出一条信息,用于检查当前是否已经存在一个流数据源(通过将 `streamSource` 转换为布尔值来判断,若已赋值则表示存在),
// 主要是为了在启动新的流数据源之前了解当前状态,便于后续做相应处理,比如如果已经存在就先停止它等操作。
grunt.log.ok('do we have a streaming source already?',!!streamSource);
grunt.log.ok('do we have a streaming source already?', !!streamSource)
// 如果 `streamSource` 已经有值(即之前已经启动过流数据源服务),则执行以下操作,先通过 `grunt.log.ok` 向控制台输出一条提示信息,表示似乎已经存在一个流数据源服务器,
// 然后调用 `streamSource.stop()` 方法(这里假设 `streamSource` 对象有 `stop` 方法来停止之前的服务)来停止它,以便释放相关资源,为启动新的流数据源服务做准备。
// if we previously loaded the streamsource, stop it to let the new one in:
if (streamSource) {
grunt.log.ok('there seems to be a streaming server already, let\'s stop it');
streamSource.stop();
grunt.log.ok('there seems to be a streaming server already, let\'s stop it')
streamSource.stop()
}
// 通过 `require` 加载 `./test/streamsource.js` 文件(这个文件应该是定义了流数据源相关的逻辑,比如启动服务的具体实现等),并将返回的对象赋值给 `streamSource` 变量,
// 然后调用 `streamSource.start` 方法(同样假设该对象有 `start` 方法),传入 `STREAM_SOURCE_PORT_HTTP`(前面定义的流数据源端口号)和 `grunt` 对象作为参数来启动流数据源服务,
// 使得后续的任务(比如测试任务等)可以基于这个流数据源进行相应的操作,比如获取测试数据等。
streamSource = require('./test/streamsource.js');
streamSource.start(STREAM_SOURCE_PORT_HTTP, grunt);
});
streamSource = require('./test/streamsource.js')
streamSource.start(STREAM_SOURCE_PORT_HTTP, grunt)
})
// 注册一个名为 `jasmine_node_oboe` 的 Grunt 任务,任务描述为 `Runs jasmine-node.`,用于运行 `jasmine-node` 相关的测试操作,
// 在任务函数内部通过调用 `runNpmScript` 函数(前面有定义,用于运行 `npm` 脚本命令)来执行 `test-node` 这个 `npm` 脚本,并传入 `this.async()`
// `this.async()` 是 Grunt 任务中用于支持异步操作的方法,它返回一个回调函数,用于告知 Grunt 任务何时完成,确保任务的异步执行逻辑能正确被 Grunt 框架识别和管理。
grunt.registerTask('jasmine_node_oboe', 'Runs jasmine-node.', function () {
runNpmScript('test-node', this.async());
});
runNpmScript('test-node', this.async())
})
// 注册一个名为 `headless-mode` 的 Grunt 任务,这个任务的作用是改变自动启动浏览器的配置,将原本默认自动启动的浏览器列表(`autoStartBrowsers`)清空,
// 然后添加 `PhantomJS` 作为唯一的自动启动浏览器,这样后续执行相关测试任务时(比如某些默认使用自动启动浏览器的 `Karma` 测试任务)就会使用 `PhantomJS` 浏览器进行测试,
// 实现了切换到无界面headless模式进行测试的功能方便在不需要实际浏览器界面展示的情况下运行测试例如在持续集成环境中或者只关注测试结果不关心界面展示的场景下使用。
// 通过执行 `grunt headless-mode default` 命令就可以在不使用实际浏览器的情况下运行测试任务(这里 `default` 可能是后续其他默认配置相关的操作,具体要看整体使用场景)。
// change the auto-starting browsers so that future tests will use
// phantomjs instead of actual browsers. Can do:
// grunt headless-mode default
// to run without any actual browsers
grunt.registerTask('headless-mode', function () {
autoStartBrowsers.length = 0;
autoStartBrowsers.push('PhantomJS');
});
autoStartBrowsers.length = 0
autoStartBrowsers.push('PhantomJS')
})
// 注册一个名为 `test-start-server` 的 Grunt 任务,该任务只包含一个子任务 `karma:persist`
// 其目的是启动一个持久化的 `Karma` 服务器,方便后续可以多次向这个服务器提交测试任务去运行,而不需要每次都重新启动服务器,常用于开发过程中持续进行测试的场景。
grunt.registerTask('test-start-server', [
'karma:persist'
]);
])
// 注册一个名为 `test-run` 的 Grunt 任务,该任务包含 `karma:persist:run` 子任务,用于在已经启动的持久化 `Karma` 服务器(通过 `test-start-server` 任务启动)上运行测试任务,
// 实现了将服务器启动和运行测试分离的操作方式,使得可以灵活控制服务器的启动时机以及测试的执行时间,提高测试执行的灵活性和效率。
grunt.registerTask('test-run', [
'karma:persist:run'
]);
])
// 注册一个名为 `dist-sizes` 的 Grunt 任务,该任务包含 `exec:reportMinifiedAndGzippedSize` 子任务,
// 其作用是执行获取压缩并经过 `gzip` 处理后的文件大小的相关命令(前面 `exec` 任务配置中有具体定义),用于查看构建生成的文件在经过相关处理后的大小情况,
// 方便开发人员监控文件大小是否符合预期,比如是否超出了某个限制等,对于优化项目构建输出有一定的帮助。
grunt.registerTask('dist-sizes', [
'exec:reportMinifiedAndGzippedSize'
]);
])
// 注册一个名为 `node-build` 的 Grunt 任务,该任务包含 `exec:createGitVersionJs` 和 `exec:webpackNode` 两个子任务,
// 主要用于执行与 Node.js 相关的构建操作,先是通过 `exec:createGitVersionJs` 创建包含 Git 版本描述信息的文件(可能用于版本管理或者在构建输出中体现版本信息等),
// 然后通过 `exec:webpackNode` 启动 Webpack 针对 Node.js 相关代码的构建任务(使用特定的 Node.js 相关的 Webpack 配置文件),完成 Node.js 代码的打包、编译等构建流程。
grunt.registerTask('node-build', [
'exec:createGitVersionJs',
'exec:webpackNode'
]);
])
// 注册一个名为 `node-build-test` 的 Grunt 任务,该任务包含 `node-build` 和 `jasmine_node_oboe` 两个子任务,
// 先是执行 `node-build` 任务完成 Node.js 相关代码的构建操作,然后执行 `jasmine_node_oboe` 任务运行 `jasmine-node` 相关的测试,实现了构建后立即进行测试的流程,
// 便于及时发现 Node.js 代码在构建后是否存在问题,保证代码质量,常用于开发过程中的持续集成和代码验证环节。
grunt.registerTask('node-build-test', [
'node-build',
'jasmine_node_oboe'
]);
])
// 注册一个名为 `node` 的 Grunt 任务,该任务包含 `start-stream-source` 和 `node-build-test` 两个子任务,
// 先启动流数据源服务(通过 `start-stream-source` 任务),为后续测试等操作提供数据支持等,然后执行 `node-build-test` 任务完成 Node.js 相关代码的构建与测试流程,
// 整合了相关前置准备和主要的构建测试操作,在整个项目构建和测试体系中作为针对 Node.js 部分的一个完整流程任务存在。
grunt.registerTask('node', [
'start-stream-source',
'node-build-test'
]);
])
// 注册一个名为 `browser-build` 的 Grunt 任务,该任务包含 `exec:createGitVersionJs` 和 `exec:webpackBrowser` 两个子任务,
// 主要用于执行与浏览器相关代码的构建操作,先是通过 `exec:createGitVersionJs` 创建包含 Git 版本描述信息的文件(同样可能用于版本管理等目的),
// 然后通过 `exec:webpackBrowser` 启动 Webpack 针对浏览器相关代码的构建任务(使用默认的浏览器相关的 Webpack 配置),完成浏览器端代码的打包、编译等构建流程,
// 以生成适合在浏览器中运行的代码文件,是浏览器端代码构建环节的重要任务配置。
grunt.registerTask('browser-build', [
'exec:createGitVersionJs',
'exec:webpackBrowser'
]);
])
// 注册一个名为 `browser-build-test` 的 Grunt 任务,该任务包含多个子任务,按照顺序依次执行 `karma:single-dev`(在默认自动启动的浏览器中运行开发相关的测试)、
// `karma:single-browser-http`(在默认自动启动的浏览器中运行基于 HTTP 相关配置的测试)、`browser-build`(执行浏览器相关代码的构建操作)、
// `karma:single-concat`(在默认自动启动的浏览器中运行与文件合并相关的测试)、`karma:single-minified`(在默认自动启动的浏览器中运行针对压缩后代码的测试)、
// `karma:single-amd`(在默认自动启动的浏览器中运行针对遵循 AMD 规范的代码的测试)等任务,实现了浏览器端代码构建、多种不同类型测试的完整流程,
// 可以全面地对浏览器端代码进行验证,确保代码在不同场景和配置下都能正常运行并符合预期,是浏览器端代码质量保障的重要任务组合。
grunt.registerTask('browser-build-test', [
'karma:single-dev',
'karma:single-browser-http',
@ -363,50 +250,44 @@ module.exports = function (grunt) {
'karma:single-concat',
'karma:single-minified',
'karma:single-amd'
]);
])
// 注册一个名为 `build` 的 Grunt 任务,该任务只包含 `exec:webpackBrowser` 子任务,主要就是启动 Webpack 针对浏览器相关代码的构建任务,
// 侧重于单纯的浏览器端代码构建操作,相对比较简洁直接,适用于只需要进行基本的浏览器代码打包、编译等场景,而不需要像 `browser-build-test` 那样执行一系列测试任务的情况。
grunt.registerTask('build', [
'exec:webpackBrowser'
]);
])
// 注册一个名为 `build-integration-test` 的 Grunt 任务,该任务包含多个子任务,先是执行 `build` 任务完成浏览器相关代码的构建,然后启动流数据源服务(通过 `start-stream-source` 任务),
// 接着在默认自动启动的浏览器中运行与文件合并相关的测试(`karma:single-concat`),再运行 `jasmine-node` 相关的测试(`jasmine_node_oboe`),最后查看构建生成文件的大小情况(`dist-sizes`
// 实现了构建、数据源准备、多种测试以及文件大小监控的完整流程,用于对整个项目进行集成测试,确保不同部分协同工作正常,代码在集成环境下的整体功能和性能符合预期。
// build and run just the integration tests.
grunt.registerTask('build-integration-test', [
'build',
'start-stream-source',
'karma:single-concat',
'jasmine_node_oboe',
'dist-sizes'
]);
])
// 注册一个名为 `default` 的 Grunt 任务,这个任务通常是在没有指定具体执行哪个任务时默认执行的任务组合,包含多个子任务,
// 先是执行 `clear`(这里不确定 `clear` 具体定义,如果存在可能是用于清理某些通用的内容,比如临时文件等,不过代码中前面未明确看到其配置,可能是遗漏或者在其他地方定义)和 `clean`(用于清理指定的文件,前面 `clean` 任务配置中有定义)任务清理相关文件和环境,
// 然后启动流数据源服务(`start-stream-source`),接着执行 `browser-build-test`(完成浏览器端代码构建与多种测试)、`node-build-test`(完成 Node.js 相关代码构建与测试),最后查看构建生成文件的大小情况(`dist-sizes`
// 整合了项目中主要的清理、构建、测试以及文件大小监控等操作,作为一个默认的完整项目构建和验证流程存在,方便在日常开发或者简单部署场景下直接使用。
grunt.registerTask('default', [
'clear',
'clean',
'start-stream-source',
'browser-build-test',
'node-build-test',
'dist-sizes'
]);
])
// 注册一个名为 `browser-test-auto-run` 的 Grunt 任务,该任务包含 `start-stream-source`、`karma:persist` 和 `concurrent:watchDev` 三个子任务,
// 先启动流数据源服务,接着启动一个持久化的 `Karma` 服务器,然后同时运行 `watchDev` 相关的多个 `watch` 任务(前面 `concurrent` 配置中有定义,用于监听文件变化并执行相应测试等任务),
// 实现了在开发过程中自动监听文件变化并自动运行浏览器相关测试的功能,开发人员只需要启动这个任务,然后捕获好相关浏览器(可能是手动启动一些浏览器并让 `Karma` 能连接到它们等操作),
// 之后在编辑代码保存时,就能自动触发测试任务运行,极大地提高了开发效率,方便及时发现代码修改对浏览器端代码功能的影响。
// browser-test-auto-run or node-test-auto-run
//
// The most useful for developing. Start this task, capture some browsers
// (unless node) then edit the code. Tests will be run as the code is
// saved.
grunt.registerTask('browser-test-auto-run', [
'start-stream-source',
'karma:persist',
'concurrent:watchDev'
]);
// 注册一个名为 `node-test-auto-run` 的 Grunt 任务,该任务包含 `start-stream-source` 和 `watch:testNode` 两个子任务,
// 先启动流数据源服务,然后启动监听 Node.js 相关代码文件变化的 `watch` 任务(前面 `watch` 任务配置中有定义),当 Node.js 相关代码文件发生变化时,
])
grunt.registerTask('node-test-auto-run', [
'start-stream-source',
'watch:testNode'

@ -1,282 +1,235 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<!-- 定义页面的字符编码为 UTF-8确保能正确显示各种字符 -->
<meta charset="UTF-8">
<!-- 设置页面视口,使其在移动设备上能自适应宽度,初始缩放比例为 1.0,方便页面在不同设备上的展示效果优化 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 设置页面标题为“创建新的投票项目”,该标题会显示在浏览器标签页上 -->
<title>创建新的投票项目</title>
<!-- 页面内部样式定义,用于设置页面元素的外观样式 -->
<style>
body {
/* 设置页面默认字体为 Arial 或无衬线字体sans-serif增强字体的通用性和兼容性 */
font-family: Arial, sans-serif;
/* 设置页面背景颜色为浅灰色(#f4f4f4营造柔和的视觉效果 */
background-color: #f4f4f4;
/* 清除页面默认的外边距,便于后续进行精确的页面布局 */
margin: 0;
/* 清除页面默认的内边距,同样利于布局控制 */
padding: 0;
}
.container {
/* 设置容器的最大宽度为 600px避免在大屏幕上内容过度拉伸保持合适的布局宽度 */
max-width: 600px;
/* 使容器在水平方向上居中显示,通过设置左右外边距为 auto 实现 */
margin: 50px auto;
/* 设置容器的背景颜色为白色(#fff与页面背景区分开来 */
background-color: #fff;
/* 设置容器内部的内边距为 20px增加内容与容器边框的间距 */
padding: 20px;
/* 设置容器的边框圆角为 8px使容器外观更圆润美观 */
border-radius: 8px;
/* 为容器添加一个淡淡的阴影效果,增加立体感,参数表示水平和垂直方向阴影偏移量为 0模糊半径为 10px阴影颜色为半透明黑色rgba(0, 0, 0, 0.1) */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
/* 使标题文本在水平方向上居中对齐 */
text-align: center;
/* 设置标题文本颜色为深灰色(#333增强可读性和视觉对比度 */
color: #333;
}
label {
/* 将 label 标签设置为块级元素,使其独占一行,方便布局和样式控制 */
display: block;
/* 设置 label 标签下方的外边距为 5px增加与下方元素的间距 */
margin-bottom: 5px;
/* 设置 label 标签文本颜色为深灰色(#333保持整体文本颜色一致性 */
color: #333;
}
input[type="text"],
select,
input[type="date"],
button {
/* 设置这些表单元素的宽度为 100%,使其占满父容器宽度,便于在不同屏幕尺寸下保持布局一致性 */
width: 100%;
/* 设置表单元素内部的内边距为 10px增加输入内容与边框的间距 */
padding: 10px;
/* 设置表单元素下方的外边距为 20px增加元素之间的垂直间距 */
margin-bottom: 20px;
/* 设置表单元素的边框为 1px 实线,颜色为浅灰色(#ccc统一外观样式 */
border: 1px solid #ccc;
/* 设置边框圆角为 5px使元素外观更圆润 */
border-radius: 5px;
/* 设置盒模型为 border-box这样元素设置的内边距和边框不会增加元素的实际宽度方便布局计算 */
box-sizing: border-box;
}
button {
/* 设置按钮的背景颜色为蓝色(#007bff通常用于表示可操作的主要按钮颜色 */
background-color: #007bff;
/* 设置按钮文本颜色为白色(#fff与背景颜色形成鲜明对比增强可读性 */
color: #fff;
/* 将鼠标指针样式设置为手型,提示用户该元素可点击操作 */
cursor: pointer;
/* 设置按钮背景颜色在鼠标悬停时的过渡效果,过渡时间为 0.3 秒过渡动画为缓动效果ease使交互体验更平滑 */
transition: background-color 0.3s ease;
}
button:hover {
/* 定义鼠标悬停在按钮上时的背景颜色变化,变为更深的蓝色(#0056b3增强交互反馈效果 */
background-color: #0056b3;
}
.message {
/* 使消息文本在水平方向上居中对齐 */
text-align: center;
/* 设置消息文本上方的外边距为 20px增加与上方元素的间距 */
margin-top: 20px;
}
.success {
/* 设置表示成功的消息文本颜色为绿色,直观地传达操作成功的信息 */
color: green;
}
.error {
/* 设置表示错误的消息文本颜色为红色,醒目地提示出现错误情况 */
color: red;
}
</style>
</head>
<body>
<!-- 创建一个类名为 container 的 div 容器,用于包裹整个投票项目创建的表单内容,实现布局和样式的统一管理 -->
<div class="container">
<!-- 页面标题,显示“创建新的投票项目”,通过 h1 标签强调其重要性,并且在样式中设置了居中对齐 -->
<h1>创建新的投票项目</h1>
<!-- 创建一个表单id 为 createVoteForm用于收集创建投票项目所需的各种信息后续通过 JavaScript 来处理表单提交等操作 -->
<form id="createVoteForm">
<!-- 创建一个 label 标签,用于提示输入投票标题,通过 for 属性与对应的 input 元素关联,增强可访问性 -->
<label for="voteTitle">投票标题:</label>
<!-- 创建一个文本输入框id 为 voteTitlename 也为 voteTitle并且设置为必填项required用于用户输入投票项目的标题 -->
<input type="text" id="voteTitle" name="voteTitle" required>
<!-- 创建一个 label 标签,用于提示选择候选项数量 -->
<label for="numOptions">候选项数量:</label>
<!-- 创建一个下拉选择框id 为 numOptionsname 为 numOptions并且添加了 onchange 事件监听器,当选项改变时会调用 updateOptions 函数来动态更新候选项的输入框,同时设置为必填项 -->
<select id="numOptions" name="numOptions" onchange="updateOptions()" required>
<!-- 下拉框的默认提示选项,显示“选择候选项数量”,其 value 值为空 -->
<option value="">选择候选项数量</option>
<!-- 可供选择的候选项数量选项,分别提供了从 2 到 10 的数值选项 -->
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
<!-- 创建一个空的 div 容器id 为 optionsContainer后续通过 JavaScript 动态生成候选项的输入框等元素会添加到这个容器内 -->
<div id="optionsContainer"></div>
<!-- 创建一个 label 标签,用于提示输入投票截止日期 -->
<label for="deadline">截止日期:</label>
<!-- 创建一个日期选择输入框id 为 deadlinename 为 deadline并且设置为必填项用于用户选择投票的截止时间 -->
<input type="date" id="deadline" name="deadline" required>
<!-- 创建一个按钮,类型为 submit文本为“创建投票”用于提交表单数据触发创建投票的相关操作按钮的样式在前面的 CSS 中已定义,有鼠标悬停等交互效果 -->
<button type="submit">创建投票</button>
</form>
<!-- 创建一个空的 div 容器id 为 message类名为 message用于后续通过 JavaScript 根据操作结果显示相应的提示消息,如投票创建成功或失败的提示 -->
<div id="message" class="message"></div>
</div>
<!-- 通过 CDN 引入 Web3.js 库的最小化版本,版本号为 1.5.3Web3.js 常用于与以太坊区块链进行交互,在本页面中可能用于处理投票相关的区块链操作 -->
<script src="https://cdn.jsdelivr.net/npm/web3@1.5.3/dist/web3.min.js"></script>
<!-- 通过 CDN 引入 MetaMask 检测提供器的最小化版本,版本号为 1.2.0,用于检测用户浏览器是否安装了 MetaMask 扩展程序,这在基于区块链的应用中很常用,因为 MetaMask 常用于管理以太坊账户等操作 -->
<script src="https://cdn.jsdelivr.net/npm/@metamask/detect-provider@1.2.0/dist/detect-provider.min.js"></script>
<script>
// 以下是 JavaScript 脚本部分的注释,用于实现页面的交互逻辑和功能
// 定义 updateOptions 函数,用于根据用户在下拉框中选择的候选项数量动态更新候选项输入框的显示情况
function updateOptions() {
// 获取下拉框id 为 numOptions中用户选择的值即候选项的数量
const numOptions = document.getElementById('numOptions').value;
// 获取用于存放候选项输入框的容器元素id 为 optionsContainer
const optionsContainer = document.getElementById('optionsContainer');
// 清空 optionsContainer 内之前可能存在的候选项输入框等元素,避免重复添加造成混乱
optionsContainer.innerHTML = '';
// 如果用户选择了具体的候选项数量(即 numOptions 不为空字符串)
if (numOptions!== '') {
// 创建一个 Set 数据结构,用于存储已添加的候选项值,利用 Set 的特性可以方便地检查值是否重复
const optionValues = new Set();
// 循环生成对应数量的候选项输入框及相关标签元素
for (let i = 1; i <= numOptions; i++) {
// 创建一个 label 标签,用于显示候选项的序号提示,如“候选项 1
const label = document.createElement('label');
label.textContent = `候选项 ${i}`;
// 将创建好的 label 标签添加到 optionsContainer 容器内
optionsContainer.appendChild(label);
// 创建一个文本输入框,用于用户输入具体的候选项内容
const input = document.createElement('input');
input.type = 'text';
input.name = `option${i}`;
input.required = true;
// 为输入框添加 input 事件监听器,用于实时检查输入的值是否重复
input.addEventListener('input', function(event) {
// 获取输入框输入的值,并移除首尾空格,确保比较时的准确性
const value = event.target.value.trim();
// 如果输入的值不为空且在 optionValues 集合中已存在(表示重复)
if (value!== '' && optionValues.has(value)) {
// 设置输入框的自定义验证提示信息,提示用户候选项的值不能重复
event.target.setCustomValidity('候选项的值不能重复');
} else {
// 如果值不重复或为空,则清除自定义验证提示信息,允许输入框正常提交
event.target.setCustomValidity('');
}
});
// 将创建好的输入框添加到 optionsContainer 容器内
optionsContainer.appendChild(input);
// 将一个空字符串添加到 optionValues 集合中,避免首次输入为空值时误判为重复(因为后续会检查是否已存在该值)
optionValues.add('');
<div class="container">
<h1>创建新的投票项目</h1>
<form id="createVoteForm">
<label for="voteTitle">投票标题:</label>
<input type="text" id="voteTitle" name="voteTitle" required>
<label for="numOptions">候选项数量:</label>
<select id="numOptions" name="numOptions" onchange="updateOptions()" required>
<option value="">选择候选项数量</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
<div id="optionsContainer"></div>
<label for="deadline">截止日期:</label>
<input type="date" id="deadline" name="deadline" required>
<button type="submit">创建投票</button>
</form>
<div id="message" class="message"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/web3@1.5.3/dist/web3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@metamask/detect-provider@1.2.0/dist/detect-provider.min.js"></script>
<script>
function updateOptions() {
const numOptions = document.getElementById('numOptions').value;
const optionsContainer = document.getElementById('optionsContainer');
optionsContainer.innerHTML = ''; // 清空之前的选项
if (numOptions !== '') {
const optionValues = new Set(); // 用于存储已添加的候选项值
for (let i = 1; i <= numOptions; i++) {
const label = document.createElement('label');
label.textContent = `候选项 ${i}`;
optionsContainer.appendChild(label);
const input = document.createElement('input');
input.type = 'text';
input.name = `option${i}`;
input.required = true;
input.addEventListener('input', function(event) {
const value = event.target.value.trim(); // 移除首尾空格
if (value !== '' && optionValues.has(value)) {
event.target.setCustomValidity('候选项的值不能重复');
} else {
event.target.setCustomValidity('');
}
});
optionsContainer.appendChild(input);
optionValues.add(''); // 添加初始值,避免首次输入为空时的重复检查
// 在每个候选项输入框后添加一个换行元素br使页面布局更清晰
optionsContainer.appendChild(document.createElement('br'));
optionsContainer.appendChild(document.createElement('br'));
}
}
}
}
// 设置截止日期输入框id 为 deadline的最小值为明天的日期确保用户不能选择早于明天的截止日期
// 获取当前日期对象
const today = new Date();
// 创建一个新的日期对象,并赋值为今天的日期,用于后续修改为明天的日期
const tomorrow = new Date(today);
// 通过设置日期对象的日期值为当前日期加 1将其修改为明天的日期
tomorrow.setDate(tomorrow.getDate() + 1);
// 将明天的日期对象转换为 ISO 8601 格式的字符串,并截取日期部分(去除时间部分),得到适合设置为 input 类型为 date 的 min 属性的值
const tomorrowISO = tomorrow.toISOString().split('T')[0];
// 将计算得到的明天日期字符串设置为截止日期输入框的 min 属性值
document.getElementById('deadline').setAttribute('min', tomorrowISO);
// 当页面的 DOM 内容加载完成后(即页面结构和元素都已加载完毕),执行以下异步函数
window.addEventListener('DOMContentLoaded', async () => {
// 检查 MetaMask 是否安装,调用 detectEthereumProvider 函数(由引入的 @metamask/detect-provider 库提供)进行检测
const provider = await detectEthereumProvider();
// 如果没有检测到 MetaMask 提供器(即未安装 MetaMask 扩展程序)
if (!provider) {
// 在控制台输出错误信息,提示用户安装 MetaMask 扩展程序
console.error('请安装 MetaMask 扩展程序');
return;
}
// 创建一个 Web3 实例传入检测到的以太坊提供器provider用于后续与以太坊区块链进行交互
const web3 = new Web3(provider);
try {
// 请求 MetaMask 授权,通过调用 ethereum.request 方法并传入指定的授权方法名eth_requestAccounts来向 MetaMask 申请使用用户账户权限
await ethereum.request({ method: 'eth_requestAccounts' });
// 获取 MetaMask 用户的账户地址,通过调用 web3.eth.getAccounts 方法,该方法返回一个包含用户账户地址的数组,通常第一个地址就是常用的主地址
const accounts = await web3.eth.getAccounts();
const metaMaskUser = accounts[0];
// 为表单id 为 createVoteForm添加 submit 事件监听器,用于处理表单提交操作,阻止表单默认的提交行为(避免页面刷新等默认操作),然后进行后续的数据处理和提交逻辑
document.getElementById('createVoteForm').addEventListener('submit', function(event) {
event.preventDefault();
// 创建一个 FormData 对象,用于获取表单中用户输入的所有数据,方便后续进行处理和发送
const formData = new FormData(this);
const data = {};
// 遍历 FormData 对象中的每一项数据,将键值对添加到 data 对象中,方便后续以 JSON 格式发送数据
formData.forEach(function(value, key) {
data[key] = value;
});
// 将获取到的 MetaMask 用户地址添加到 data 对象中,以便后续将该信息一起发送到服务器,可能用于标识投票创建者等用途
data.metaMaskUser = metaMaskUser;
// 设置截止日期的最小值为明天的日期
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1); // 设置日期为明天
const tomorrowISO = tomorrow.toISOString().split('T')[0];
document.getElementById('deadline').setAttribute('min', tomorrowISO);
window.addEventListener('DOMContentLoaded', async () => {
// 检查 MetaMask 是否安装
const provider = await detectEthereumProvider();
if (!provider) {
console.error('请安装 MetaMask 扩展程序');
return;
}
// 检查候选项是否存在重复值,创建一个新的 Set 数据结构用于存储已检查的候选项值
const optionValues = new Set();
let hasDuplicate = false;
for (let i = 1; i <= data.numOptions; i++) {
// 获取每个候选项的值,并移除首尾空格,确保准确比较
const option = data[`option${i}`].trim();
// 如果候选项值不为空
if (option!== '') {
// 如果该值已经在 optionValues 集合中存在(表示重复)
if (optionValues.has(option)) {
hasDuplicate = true;
break;
} else {
// 如果值不重复,则将其添加到 optionValues 集合中
optionValues.add(option);
// 创建 Web3 实例
const web3 = new Web3(provider);
try {
// 请求 MetaMask 授权
await ethereum.request({ method: 'eth_requestAccounts' });
// 获取 MetaMask 用户地址
const accounts = await web3.eth.getAccounts();
const metaMaskUser = accounts[0];
// 将 MetaMask 用户地址添加到表单数据中
document.getElementById('createVoteForm').addEventListener('submit', function(event) {
event.preventDefault();
const formData = new FormData(this); // 获取表单数据
const data = {};
formData.forEach(function(value, key) {
data[key] = value;
});
// 添加 MetaMask 用户地址到数据中
data.metaMaskUser = metaMaskUser;
// 检查候选项是否重复
const optionValues = new Set();
let hasDuplicate = false;
for (let i = 1; i <= data.numOptions; i++) {
const option = data[`option${i}`].trim(); // 移除首尾空格
if (option !== '') {
if (optionValues.has(option)) {
hasDuplicate = true;
break;
} else {
optionValues.add(option);
}
}
}
}
// 如果候选项存在重复值
if (hasDuplicate) {
// 弹出警告框,提示用户候选项的值不能重复
alert('候选项的值不能重复');
} else {
// 如果候选项没有重复,进行表单数据的提交操作
// 使用 fetch API 发送一个 POST 请求到服务器的 /createVote 路径,用于创建投票项目
fetch('/createVote', {
method: 'POST',
// 设置请求头的 Content-Type 为 application/json表示发送的数据是 JSON 格式
headers: {
'Content-Type': 'application/json'
},
// 将 data 对象转换为 JSON 字符串
if (hasDuplicate) {
alert('候选项的值不能重复');
} else {
// 候选项没有重复,提交表单
fetch('/createVote', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应错误');
}
return response.json();
})
.then(data => {
const messageElement = document.getElementById('message');
if (data.success) {
messageElement.textContent = `投票项目创建成功!合约地址:${data.contractAddress}`;
messageElement.classList.add('success');
// 将合约地址复制到剪贴板
navigator.clipboard.writeText(data.contractAddress)
.then(() => console.log('合约地址已复制到剪贴板'))
.catch(err => console.error('复制到剪贴板失败:', err));
// Redirect to index.html on successful creation
setTimeout(() => {
window.location.href = '/index.html';
}, 1000); // Redirect after 2 seconds
} else {
messageElement.textContent = '投票项目创建失败,请稍后重试。';
messageElement.classList.add('error');
}
})
.catch(error => {
console.error('发生错误:', error);
const messageElement = document.getElementById('message');
messageElement.textContent = '发生错误,请稍后重试。';
messageElement.classList.add('error');
});
}
});
} catch (error) {
console.error('授权失败:', error);
}
});
</script>
</body>
</html>

Loading…
Cancel
Save