Newer
Older
# Copyright 2019 PrivateStorage.io, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
A module for logic controlling the manner in which ZKAPs are spent.
"""
from zope.interface import (
Interface,
Attribute,
implementer,
)
import attr
from .eliot import (
GET_PASSES,
)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
class IPassGroup(Interface):
"""
A group of passed meant to be spent together.
"""
passes = Attribute(":ivar list[Pass] passes: The passes themselves.")
def split(select_indices):
"""
Create two new ``IPassGroup`` providers. The first contains all passes in
this group at the given indices. The second contains all the others.
:param list[int] select_indices: The indices of the passes to include
in the first resulting group.
:return (IPassGroup, IPassGroup): The two new groups.
"""
def expand(by_amount):
"""
Create a new ``IPassGroup`` provider which contains all of this group's
passes and some more.
:param int by_amount: The number of additional passes the resulting
group should contain.
:return IPassGroup: The new group.
"""
def mark_spent():
"""
The passes have been spent successfully. Ensure none of them appear in
any ``IPassGroup`` provider created in the future.
:return: ``None``
"""
def mark_invalid(reason):
"""
The passes could not be spent. Ensure none of them appear in any
``IPassGroup`` provider created in the future.
:param unicode reason: A short description of the reason the passes
could not be spent.
:return: ``None``
"""
def reset():
"""
The passes have not been spent. Return them to for use in a future
``IPassGroup`` provider.
:return: ``None``
"""
class IPassFactory(Interface):
"""
An object which can create passes.
"""
def get(message, num_passes):
"""
:param unicode message: A request-binding message for the resulting passes.
:param int num_passes: The number of passes to request.
:return IPassGroup: A group of passes bound to the given message and
of the requested size.
"""
@implementer(IPassGroup)
@attr.s
class PassGroup(object):
"""
Track the state of a group of passes intended as payment for an operation.
:ivar unicode _message: The request binding message for this group of
passes.
:ivar IPassFactory _factory: The factory which created this pass group.
:ivar list[Pass] passes: The passes of which this group consists.
"""
_message = attr.ib()
_factory = attr.ib()
passes = attr.ib()
def split(self, select_indices):
selected = []
unselected = []
for idx, p in enumerate(self.passes):
if idx in select_indices:
selected.append(p)
else:
unselected.append(p)
return (
attr.evolve(self, passes=selected),
attr.evolve(self, passes=unselected),
)
def expand(self, by_amount):
return attr.evolve(
self,
passes=self.passes + self._factory.get(self._message, by_amount).passes,
)
def mark_spent(self):
self._factory._mark_spent(self.passes)
def mark_invalid(self, reason):
self._factory._mark_invalid(reason, self.passes)
def reset(self):
self._factory._reset(self.passes)
@attr.s
class SpendingController(object):
"""
A ``SpendingController`` gives out ZKAPs and arranges for re-spend
attempts when necessary.
"""
extract_unblinded_tokens = attr.ib()
tokens_to_passes = attr.ib()
def get(self, message, num_passes):
unblinded_tokens = self.extract_unblinded_tokens(num_passes)
passes = self.tokens_to_passes(message, unblinded_tokens)
GET_PASSES.log(
message=message,
count=num_passes,
)
return PassGroup(message, self, passes)
def _mark_spent(self, group):
# TODO
pass
def _mark_invalid(self, reason, group):
# TODO
pass
def _reset(self, group):
# TODO
pass