active_hashcash 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -1
- data/README.md +2 -0
- data/lib/active_hashcash/engine.rb +19 -0
- data/lib/active_hashcash/stamp.rb +52 -0
- data/lib/active_hashcash/store.rb +25 -0
- data/lib/active_hashcash/version.rb +3 -0
- data/lib/active_hashcash.rb +15 -87
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81d808ca54efb58e16c61524bde0aee355f5a7b003ef4cb6cda2e9b672c7a284
|
4
|
+
data.tar.gz: 6ec6f99dd02e353bb4e86bb9db8e65ecf35dc46e13051e401b62608700b7fde5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17f8b181f15b009b850fea2e1ead3a5cb904218d8877b31d9b6a2e5f2578972a6a1a2837cdbda46013987911a88198d543dd7f3a79f68b455a969a005a3fbfc8
|
7
|
+
data.tar.gz: 2b02561bf0516b2accb42ac64fe8d93b7bf956bb9474fcadec93db08f4be551d9eea430884d133d08b92156a9d10440ac8f48067207c95d3b72e4268752748dd
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -66,6 +66,8 @@ You must have Redis in order to prevent double spent stamps. Otherwise it will b
|
|
66
66
|
It automatically tries to connect with the environement variables `ACTIVE_HASHCASH_REDIS_URL` or `REDIS_URL`.
|
67
67
|
You can also manually set the URL with `ActiveHashcash.redis_url = redis://user:password@localhost:6379`.
|
68
68
|
|
69
|
+
You should call `ActiveHashcash::Store#clean` once a day, to remove expired stamps.
|
70
|
+
|
69
71
|
## Complexity
|
70
72
|
|
71
73
|
Complexity is the most important parameter. By default its value is 20 and requires most of the time 5 to 20 seconds to be solved on a decent laptop.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveHashcash
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
config.assets.paths << File.expand_path("../..", __FILE__)
|
4
|
+
|
5
|
+
config.after_initialize { load_translations }
|
6
|
+
|
7
|
+
def load_translations
|
8
|
+
if !I18n.backend.exists?(I18n.locale, "active_hashcash")
|
9
|
+
I18n.backend.store_translations(:de, {active_hashcash: {waiting_label: "Warten auf die Überprüfung ..."}})
|
10
|
+
I18n.backend.store_translations(:en, {active_hashcash: {waiting_label: "Waiting for verification ..."}})
|
11
|
+
I18n.backend.store_translations(:es, {active_hashcash: {waiting_label: "A la espera de la verificación ..."}})
|
12
|
+
I18n.backend.store_translations(:fr, {active_hashcash: {waiting_label: "En attente de vérification ..."}})
|
13
|
+
I18n.backend.store_translations(:it, {active_hashcash: {waiting_label: "In attesa di verifica ..."}})
|
14
|
+
I18n.backend.store_translations(:jp, {active_hashcash: {waiting_label: "検証待ち ..."}})
|
15
|
+
I18n.backend.store_translations(:pt, {active_hashcash: {waiting_label: "À espera de verificação ..."}})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActiveHashcash
|
2
|
+
class Stamp
|
3
|
+
attr_reader :version, :bits, :date, :resource, :extension, :rand, :counter
|
4
|
+
|
5
|
+
def self.parse(string)
|
6
|
+
args = string.split(":")
|
7
|
+
new(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.mint(resource, options = {})
|
11
|
+
new(
|
12
|
+
options[:version] || 1,
|
13
|
+
options[:bits] || ActiveHashcash.bits,
|
14
|
+
options[:date] || Date.today.strftime("%y%m%d"),
|
15
|
+
resource,
|
16
|
+
options[:ext],
|
17
|
+
options[:rand] || SecureRandom.alphanumeric(16),
|
18
|
+
options[:counter] || 0).work
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(version, bits, date, resource, extension, rand, counter)
|
22
|
+
@version = version
|
23
|
+
@bits = bits.to_i
|
24
|
+
@date = date.respond_to?(:strftime) ? date.strftime("%y%m%d") : date
|
25
|
+
@resource = resource
|
26
|
+
@extension = extension
|
27
|
+
@rand = rand
|
28
|
+
@counter = counter
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid?
|
32
|
+
Digest::SHA1.hexdigest(to_s).hex >> (160-bits) == 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def verify(resource, bits, date)
|
36
|
+
self.resource == resource && self.bits >= bits && parse_date >= date && valid?
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
[version, bits, date, resource, extension, rand, counter].join(":")
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_date
|
44
|
+
Date.strptime(date, "%y%m%d")
|
45
|
+
end
|
46
|
+
|
47
|
+
def work
|
48
|
+
@counter += 1 until valid?
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveHashcash
|
2
|
+
class Store
|
3
|
+
attr_reader :redis
|
4
|
+
|
5
|
+
def initialize(redis = Redis.new(url: ActiveHashcash.redis_url || ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]))
|
6
|
+
@redis = redis
|
7
|
+
end
|
8
|
+
|
9
|
+
def add?(stamp)
|
10
|
+
redis.sadd("active_hashcash_stamps_#{stamp.date}", stamp) ? self : nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
redis.del(redis.keys("active_hashcash_stamps*"))
|
15
|
+
end
|
16
|
+
|
17
|
+
def clean
|
18
|
+
today = Date.today.strftime("%y%m%d")
|
19
|
+
yesterday = (Date.today - 1).strftime("%y%m%d")
|
20
|
+
keep = ["active_hashcash_stamps_#{today}", "active_hashcash_stamps_#{yesterday}"]
|
21
|
+
keys = redis.keys("active_hashcash_stamps*")
|
22
|
+
redis.del(keys - keep)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/active_hashcash.rb
CHANGED
@@ -9,14 +9,22 @@ module ActiveHashcash
|
|
9
9
|
|
10
10
|
mattr_accessor :bits, instance_accessor: false, default: 20
|
11
11
|
mattr_accessor :resource, instance_accessor: false
|
12
|
-
mattr_accessor :redis_url, instance_accessor: false
|
12
|
+
mattr_accessor :redis_url, instance_accessor: false, default: ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]
|
13
|
+
|
14
|
+
def self.store
|
15
|
+
@store ||= Store.new(Redis.new(url: ActiveHashcash.redis_url))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.store=(store)
|
19
|
+
@store = store
|
20
|
+
end
|
13
21
|
|
14
22
|
# TODO: protect_from_brute_force bits: 20, exception: ActionController::InvalidAuthenticityToken, with: :handle_failed_hashcash
|
15
23
|
|
16
24
|
# Call me via a before_action when the form is submitted : `before_action :chech_hashcash, only: :create`
|
17
25
|
def check_hashcash
|
18
|
-
|
19
|
-
|
26
|
+
stamp = hashcash_param && Stamp.parse(hashcash_param)
|
27
|
+
if stamp && stamp.verify(hashcash_resource, hashcash_bits, Date.yesterday) && ActiveHashcash.store.add?(stamp)
|
20
28
|
hashcash_after_success
|
21
29
|
else
|
22
30
|
hashcash_after_failure
|
@@ -63,88 +71,8 @@ module ActiveHashcash
|
|
63
71
|
options = {resource: hashcash_resource, bits: hashcash_bits, waiting_message: hashcash_waiting_message}
|
64
72
|
hidden_field_tag(name, "", "data-hashcash" => options.to_json)
|
65
73
|
end
|
66
|
-
|
67
|
-
def hashcash_redis
|
68
|
-
@hashcash_redis = Redis.new(url: hashcash_redis_url)
|
69
|
-
end
|
70
|
-
|
71
|
-
def hashcash_redis_url
|
72
|
-
ActiveHashcash.redis_url || ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]
|
73
|
-
end
|
74
|
-
|
75
|
-
def hashcash_stamp_is_valid?
|
76
|
-
stamp = hashcash_param && Stamp.parse(hashcash_param)
|
77
|
-
stamp && stamp.valid? && stamp.bits >= hashcash_bits && stamp.parse_date >= Date.yesterday
|
78
|
-
end
|
79
|
-
|
80
|
-
def hashcash_stamp_spent?
|
81
|
-
hashcash_redis.sismember("active_hashcash_stamps".freeze, hashcash_param)
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
class Engine < ::Rails::Engine
|
87
|
-
config.assets.paths << File.expand_path("..", __FILE__)
|
88
|
-
|
89
|
-
config.after_initialize { load_translations }
|
90
|
-
|
91
|
-
def load_translations
|
92
|
-
if !I18n.backend.exists?(I18n.locale, "active_hashcash")
|
93
|
-
I18n.backend.store_translations(:de, {active_hashcash: {waiting_label: "Warten auf die Überprüfung ..."}})
|
94
|
-
I18n.backend.store_translations(:en, {active_hashcash: {waiting_label: "Waiting for verification ..."}})
|
95
|
-
I18n.backend.store_translations(:es, {active_hashcash: {waiting_label: "A la espera de la verificación ..."}})
|
96
|
-
I18n.backend.store_translations(:fr, {active_hashcash: {waiting_label: "En attente de vérification ..."}})
|
97
|
-
I18n.backend.store_translations(:it, {active_hashcash: {waiting_label: "In attesa di verifica ..."}})
|
98
|
-
I18n.backend.store_translations(:jp, {active_hashcash: {waiting_label: "検証待ち ..."}})
|
99
|
-
I18n.backend.store_translations(:pt, {active_hashcash: {waiting_label: "À espera de verificação ..."}})
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class Stamp
|
105
|
-
attr_reader :version, :bits, :date, :resource, :extension, :rand, :counter
|
106
|
-
|
107
|
-
def self.parse(string)
|
108
|
-
args = string.split(":")
|
109
|
-
new(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
110
|
-
end
|
111
|
-
|
112
|
-
def self.mint(resource, options = {})
|
113
|
-
new(
|
114
|
-
options[:version] || 1,
|
115
|
-
options[:bits] || ActiveHashcash.bits,
|
116
|
-
options[:date] || Date.today.strftime("%y%m%d"),
|
117
|
-
resource,
|
118
|
-
options[:ext],
|
119
|
-
options[:rand] || SecureRandom.alphanumeric(16),
|
120
|
-
options[:counter] || 0).work
|
121
|
-
end
|
122
|
-
|
123
|
-
def initialize(version, bits, date, resource, extension, rand, counter)
|
124
|
-
@version = version
|
125
|
-
@bits = bits.to_i
|
126
|
-
@date = date.respond_to?(:strftime) ? date.strftime("%y%m%d") : date
|
127
|
-
@resource = resource
|
128
|
-
@extension = extension
|
129
|
-
@rand = rand
|
130
|
-
@counter = counter
|
131
|
-
end
|
132
|
-
|
133
|
-
def valid?
|
134
|
-
Digest::SHA1.hexdigest(to_s).hex >> (160-bits) == 0
|
135
|
-
end
|
136
|
-
|
137
|
-
def to_s
|
138
|
-
[version, bits, date, resource, extension, rand, counter].join(":")
|
139
|
-
end
|
140
|
-
|
141
|
-
def parse_date
|
142
|
-
Date.strptime(date, "%y%m%d")
|
143
|
-
end
|
144
|
-
|
145
|
-
def work
|
146
|
-
@counter += 1 until valid?
|
147
|
-
self
|
148
|
-
end
|
149
|
-
end
|
150
74
|
end
|
75
|
+
|
76
|
+
require "active_hashcash/stamp"
|
77
|
+
require "active_hashcash/store"
|
78
|
+
require "active_hashcash/engine"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_hashcash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexis Bernard
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -50,6 +50,10 @@ files:
|
|
50
50
|
- LICENSE.txt
|
51
51
|
- README.md
|
52
52
|
- lib/active_hashcash.rb
|
53
|
+
- lib/active_hashcash/engine.rb
|
54
|
+
- lib/active_hashcash/stamp.rb
|
55
|
+
- lib/active_hashcash/store.rb
|
56
|
+
- lib/active_hashcash/version.rb
|
53
57
|
- lib/hashcash.js
|
54
58
|
homepage: https://github.com/BaseSecrete/active_hashcash
|
55
59
|
licenses:
|