- Constructor: Define el poseedor del contrato como aquel que deploya el contrato, emite el evento Deploy
- stamp(string clave, string valor) -> void : el contrato guarda valor y bloque en un diccionario para la clave dada, si la clave ya estaba asignada, no hace nada. Emite el evento Stamped, en el caso que sí se haga la asignación, con los parámetros indexados sobre la clave, el valor y la cuenta que envió la transacción
- verify(string clave, string valor) -> bool : verifica que para la clave dada, tiene el valor asignado en el diccionario
- getBlock(string clave) -> unsigned int : precondición: está definido el diccionario, devuelve el int que corresponde al número de bloque donde se hizo el stamp original
- selfDestroy() -> void : precondición: el usuario es el poseedor del contrato. Invoca el selfdestroy del contrato, y se envía todo el eter que tenga al poseedor del contrato, emite el evento SelfDestroy con esos parámetros
Notas:
- Se debería hacer un call del verify antes de hacer una transacción de stamp, para que en el caso de que ya exista, no consumir gas innecesario
# Contrato de distribución de ether
peronEther.sol
### Sobre la problemática:
Sobre redes con consenso de Proof of Authority, al no tener mecanismo de emisión de token descentralizado, es necesario tener una idea de cómo distribuir el ether. Y esta herramienta, tiene que permitir entregar ether a aquellos que NO tengan capacidad de generarlo, y que además tenga restricciones para, al monitorear abusos, limitar o incluso expulsar a los receptores de ether.
Además, el otro problema es verificar que las cuentas que corresponden a los beneficiarios, efectivamente están bajo control, o más bien, que sus claves privadas estén asignadas a gente que tenga acceso a ellas, para no entregar ether a claves públicas sin control
### Funcional:
La idea de este contrato es tener una herramienta de entrega de ether, con un sistema de autorización sencillo.
Se identifican 3 roles:
- Beneficiario: un account sobre el que se le va a enviar ether según el criterio del distribuidor
- Distribuidor: un account que puede asignar, eliminar y modificar topes de beneficiarios, y puede hacer los reabastecimientos
- Owner: el account que hace el deploy del contrato es quien tiene este rol, y es quien puede agregar, eliminar y modificar topes de distribuidores y beneficiarios
Cada beneficiario tiene un tope, y si en el momento de reabastecimiento, se le dará la diferencia entre su balance y el tope. Este es asignado por el distribuidor que lo agregó, quien también puede modificarlo en cualquier momento, y a su vez, el tope que puede asignar un distribuidor a un beneficiario no puede ser mayor que su propio tope, que es finalmente decidido por el owner. Esto es para asegurar que los distribuidores no asignan límites de balance demasiado altos a los beneficiarios.
En cualquier momento un distribuidor puede reabastecer a todos, algunos o un beneficiario, si el balance del contrato es suficiente para la acción
Sobre la verificación de las cuentas, es un paso difícil, porque naturalmente la blockchain como tecnología piensa en las cuentas como pares de claves públicas/privadas para desacoplar el hecho de que hay un ser humano detrás con el control de la privada, y por lo tanto de la cuenta y todas sus acciones. Es parte de la problemática entender que la asociación de una cuenta implica que hay alguien que dice tener el control sobre la cuenta a asociar, y que de hecho lo tiene.
Para eso, el contrato tiene un servicio para agregar candidatos, que realiza los siguientes pasos:
- Agregar un candidato a beneficiario, y el hash de una clave generada según un criterio arbitrario a nivel dapp, aunque esta clave tiene que ser universalmente única, y esa clave es revelada al usuario a nivel dapp
- A ese candidato, se le pasa una cantidad mínima de ether
- El candidato invoca al contrato, lo cual puede hacer porque se le habilitó el saldo suficiente, y pasa la clave sin aplicarle el hash al contrato, el contrato hashea la clave y compara con la pasada originalmente, si coincide, el candidato es aprobado
La decisión de pasar el hash al contrato al principio es porque, como en blockchain todo es público, pasar la clave en ese momento no sirve para distinguir porque cualquiera la puede ver en cualquier momento, por eso el hash. Además en el momento que el candidato haga la verificación, ahí si pasa la clave original, porque si la clave es de hecho la correcta, el contrato la hashea y si coincide entonces ya está aprobado el candidato, y no hay problema con que la clave esté pública ya que no tiene utilidad
### Interfaz:
- Constructor: define el poseedor del contrato
- addDistributor(address distr, unsigned int tope) -> bool : precondición el account es el owner. distr es agregado al listado de distribuidores con tope como su límite de asignación
- kickDistributor(address distr) -> bool : devuelve True si distr es un distribuidor y el account es el owner, caso contrario devuelve False. Quita a distr del listado
- addBeneficiary(address ben, unsigned int top) -> bool : devuelve True si ben no es distribuidor ni owner, y el account que está haciendo la transacción sí es distribuidor u owner, tope es menor o igual al tope del account haciendo la transacción, caso contrario devuelve False. Agrega el beneficiario al listado de beneficiarios
- kickBeneficiary(address ben) -> bool : devuelve True si el beneficiario está en el listado de beneficiarios y el account haciendo la transacción es distribuidor u owner, caso contrario devuelve False. Elimina ben del listado de beneficiarios
- changeLimit(address ben, unsigned int top) -> bool: devuelve True si ben es beneficiario, el account haciendo la transacción es distribuidor u owner, y tope es menor al tope asignado al distribuidor, caso contrario devuelve False. Cambia el tope del beneficiario
- replenish(address ben, unsigned int amount) -> bool: devuelve True si ben es beneficiario, el account haciendo la transacción es distribuidor, el balance de ben es menor a su tope y el balance del contrato es mayor a amount, caso contrario devuelve False. Transfiere amount ether a ben
- getDistributorLimit(address dist) -> unsigned int: devuelve el tope de asignación del distribuidor
- getBeneficiaryLimit(address ben) -> unsigned int: devuelve el tope a recibir del beneficiario
- getBeneficiaryCount() -> unsigned int: devuelve la cantidad de beneficiarios
- addSuitor(address add, string password) -> bool: devuelve true si el contrato tiene suficiente ether para transferir al candidato, y false en caso contrario. NO IMPLEMENTADO
- proveControl() -> bool: NO IMPLEMENTADO
Los métodos replenishAll y replenishList tienen funcionalidad similar a la de replenish pero para hacer reabastecimientos masivos
Notas:
- Todos los métodos tienen valor de return para poder hacer un call antes de una transacción para verificar su funcionamiento y lograr ahorrar gas
### Comparando con el SC original
Aunque conceptualmente, ambos contratos son similares, porque tienen una lista de beneficiarios y un servicio para distribuir el ether, sin embargo presentan las sig diferencias:
- El original tiene un sistema de permisos más limitado, todo el poder concetrado en un sólo admin, y aunque en este tipo de contextos, donde sistemas de autorización son más difíciles de definir, este contrato es un poco más flexible, al tener administradores con limites de asignación de tope distintos
- El método distribute del original, entrega indiscriminadamente la diferencia entre el balance y el tope de cada beneficiario, lo cual puede no ser necesariamente la intención, en el nuevo ademaś de tener este servicio, presenta otros métodos que ofrecen más granularidad, para reabastecer cuentas específicas una cantidad de ether permitida que no sea necesariamente la diferencia
- El nuevo tiene separadas las funcionalidades de setEtherAllowance en varios métodos para facilitar el uso
- El nuevo, en los métodos que tienen loops implementados, no se hace verificación de gas, porque es algo que se puede estimar, ya que los casos son los sigs:
- Entre invocaciones no se hizo transacción que altere el tamaño de los beneficiarios, entonces al usar estimateGas del lado de geth o web3, es la estimación precisa
- Si se hacen invocaciones que alteran el tamaño de los beneficiarios, y esas transacciones están pendientes, se puede tomar estimar el costo de esas transacciones y agregarlo al de esta
### TODOs
- [x] Mecanismo de verificación de cuenta, para verificar que los beneficiarios a agregar son cuentas con clave privada bajo control, todavía queda decidir si esto es parte de este smart contract
- [ ] Implementar addSuitor y proveControl, que son los métodos para el mecanismo de verificación de cuenta