Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
D
destileria2
Manage
Activity
Members
Labels
Plan
Issues
0
Issue boards
Milestones
Wiki
Code
Merge requests
0
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
blockchain
destileria2
Commits
246f2dde
Commit
246f2dde
authored
4 years ago
by
Robert Martin-Legene
Browse files
Options
Downloads
Patches
Plain Diff
Major rewrite
parent
409b19cb
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
destileria2.sol
+300
-201
300 additions, 201 deletions
destileria2.sol
with
300 additions
and
201 deletions
destileria2.sol
+
300
−
201
View file @
246f2dde
// vim:filetype=javascript
// SPDX-License-Identifier: GPL-2.0-or-later
/*
/*
Copyright 2020 de la Dirección General de Sistemas Informáticos – Secretaría Legal y Técnica - Nación.
Copyright 2020 de la Dirección General de Sistemas Informáticos – Secretaría Legal y Técnica - Nación.
...
@@ -9,231 +13,326 @@ You should have received a copy of the GNU General Public License along with thi
...
@@ -9,231 +13,326 @@ You should have received a copy of the GNU General Public License along with thi
*/
*/
pragma
solidity
>
0.4
;
pragma
solidity
>
0.4
;
contract
destileria2
{
enum
UserType
{
UserOwner
,
UserDistributor
,
UserBeneficiary
,
UserNotFound
};
// no hace falte 'indexed' ya que solamente va a haber
// un evento de este tipo
event
deployed
(
address
creator
);
// Cuando un usuario / una distribuidora fue agregado al registro
event
added
(
address
indexed
orderGiver
,
address
indexed
victim
,
UserType
usertype
,
uint256
limit
);
// Cuando un usuario fue sacado del registro
event
kicked
(
address
indexed
orderGiver
,
address
indexed
victim
);
event
limitChanged
(
address
indexed
orderGiver
,
address
indexed
victim
,
uint256
topuplimit
);
event
replenished
(
address
indexed
orderGiver
,
address
indexed
victim
,
uint256
amountGiven
);
struct
UserEntry
{
address
addr
;
UserType
userType
;
uint256
topUpLimit
;
bool
deleted
;
}
address
public
owner
;
mapping
(
address
=>
uint256
)
public
addressToUserEntryIndex
;
UserEntry
[]
public
userIndex
;
// Used for reclaiming unused positions in the userIndex
uint256
[]
public
deletedEntries
;
// Last time we did replenishAll, where did we stop?
uint256
allstopmarker
=
0
;
contract PeronEther {
constructor
()
payable
public
{
event addedBeneficiary(address indexed distr, address indexed added, uint256 limit);
event addedDistributor(address indexed added, uint256 top);
event kickedBeneficiary(address indexed distr, address indexed kicked);
event kickedDistributor(address indexed kicked);
event limitChanged(address indexed distr, address indexed ben, uint256 newLimit);
event replenished(address indexed distr, uint256 amount);
address owner;
itmap beneficiaries;
itmap distributors;
constructor() payable public {
owner
=
msg
.
sender
;
owner
=
msg
.
sender
;
// Because of the problems of using index 0 for real data
// I initially set it to just be a dummy (empty) entry.
// But we actually need an "empty" object to be returned
// when no matches are found, when people search for something
// which does not exist. I created an object that I could return
// in those cases and stored it in a separate variable, but
// then I realized that this is perfect for storing in the
// userIndex, because it is of that type. Oh, and index 0
// is empty.. can store it there! Oh, and when we search
// for a key in the mapping and there's no match, it returns
// zero. It seems like it is ideal for this job and will even
// make the code smaller
// Make first entry always empty (index 0)
userIndex
.
push
(
UserEntry
({
// This entry can be returned when no matches were found
addr
:
"
0x0000000000000000000000000000000000000000
"
,
userType
:
UserNotFound
,
topUpLimit
:
0
,
deleted
:
true
}));
}
}
modifier onlyOwner {
require( msg.sender == owner );
receive
()
external
payable
{}
modifier
onlyOwner
{
require
(
msg
.
sender
==
owner
);
_
;
_
;
}
}
modifier onlyDistributor {
require (contains(distributors, msg.sender) || msg.sender == owner);
modifier
onlyDistributorOrOwner
{
require
(
contains
(
UserDistributor
,
msg
.
sender
)
||
msg
.
sender
==
owner
);
_
;
_
;
}
}
function hash(string key) public pure returns(bytes32){
function
addBeneficiary
(
address
addr
,
uint256
topuplimit
)
public
onlyDistributorOrOwner
returns
(
bool
)
return keccak256(abi.encodePacked(key));
{
if
(
topuplimit
==
0
)
return
false
;
// User must not exist already
if
(
getUserType
(
addr
)
!=
UserNotFound
)
return
false
;
if
(
topuplimit
>
getTopUpLimit
(
UserDistributor
,
msg
.
sender
)
return
false
;
emit
added
(
msg
.
sender
,
addr
,
UserBeneficiary
,
topuplimit
);
return
!
insert
(
UserBeneficiary
,
addr
,
topuplimit
);
}
}
function addBeneficiary(address acc, uint256 limit) public onlyDistributor returns(bool){
function
addDistributor
(
address
addr
,
uint256
topuplimit
)
public
onlyOwner
returns
(
bool
)
if (!contains(distributors, acc) && limit < get(distributors, msg.sender)){
{
emit addedBeneficiary(msg.sender, acc, limit);
if
(
topuplimit
==
0
)
return !insert(beneficiaries, acc, limit);
return
false
;
}
// User must not exist already
return false;
if
(
getUserType
(
addr
)
!=
UserNotFound
)
return
false
;
emit
added
(
msg
.
sender
,
addr
,
UserDistributor
,
topuplimit
);
return
!
insert
(
UserDistributor
,
addr
,
topuplimit
);
}
}
function
addDistributor(address acc, uint256 top) public onlyOwner
returns(bool)
{
function
kick
(
UserType
victimtype
,
address
addr
)
private
returns
(
bool
)
if (!contains(beneficiaries, acc))
{
{
emit addedDistributor(acc, top);
if
(
!
contains
(
victimtype
,
addr
))
return
!insert(distributors, acc, top);
return
false
;
}
emit
kicked
(
msg
,
sender
,
addr
);
return
false
;
return
remove
(
victimtype
,
addr
)
;
}
}
function kickDistributor(address acc) public onlyOwner returns(bool){
function
kickDistributor
(
address
addr
)
public
onlyOwner
returns
(
bool
)
if (contains(distributors, acc)){
{
emit kickedDistributor(acc);
return
kick
(
UserDistributor
,
addr
);
return remove(distributors, acc);
}
return false;
}
}
function kickBeneficiary(address acc) public onlyOwner returns(bool){
function
kickBeneficiary
(
address
addr
)
public
onlyDistributorOrOwner
returns
(
bool
)
if (contains(distributors, acc)){
{
emit kickedBeneficiary(msg.sender, acc);
return
kick
(
UserBeneficiary
,
addr
);
return remove(distributors, acc);
}
return false;
}
}
function changeLimit(address acc, uint256 limit) public onlyDistributor returns(bool){
function
changeLimit
(
address
addr
,
uint256
topuplimit
)
public
onlyDistributorOrOwner
returns
(
bool
)
if (contains(beneficiaries, acc) && get(distributors, msg.sender) > limit){
{
modify(beneficiaries, acc, limit);
uint256
pos
=
addressToUserEntryIndex
[
addr
];
emit limitChanged(msg.sender, acc, limit);
if
(
pos
==
0
)
return true;
return
false
;
UserEntry
entry
=
userIndex
[
pos
];
if
(
entry
.
userType
!=
UserDistributor
&&
entry
.
userType
!=
UserBeneficiary
)
return
false
;
if
(
msg
.
sender
!=
owner
)
{
// Only our owner can change limits for Distributors
if
(
entry
.
userType
==
UserDistributor
)
return
false
;
// Distributors must obey the limit
if
(
topuplimit
>
getTopUpLimit
(
UserDistributor
,
msg
.
sender
)
return
false
;
}
}
return false;
// all is fine - now we change the limit
userIndex
[
pos
].
topUpLimit
=
topuplimit
;
emit
limitChanged
(
msg
.
sender
,
addr
,
topuplimit
);
return
true
;
}
}
function replenish(address acc, uint256 amount) public payable onlyDistributor returns(bool){
function
replenish
(
address
victim
,
uint256
amount
)
public
payable
onlyDistributorOrOwner
returns
(
bool
)
if(contains(beneficiaries,acc)
{
&& acc.balance < get(beneficiaries,acc)
if
(
getUserType
(
victim
)
!=
UserBeneficiary
)
&& address(this).balance > amount){
return
false
;
acc.transfer(amount);
uint256
topuplimit
=
getTopUpLimit
(
UserBeneficiary
,
victim
);
emit replenished(msg.sender, amount);
// Does the destination account have less than his limit?
return true;
if
(
victim
.
balance
+
amount
>
topuplimit
)
}
return
false
;
return false;
// avoid uint256 overflow
if
(
victim
.
balance
+
amount
<
victim
.
balance
)
return
false
;
// Do we have enough ether to send?
if
(
address
(
this
).
balance
<
amount
)
return
false
;
// Do the transfer and emit an event
address
payable
addrpayable
=
address
(
uint160
(
victim
))
addrpayable
.
transfer
(
amount
);
emit
replenished
(
msg
.
sender
,
victim
,
amount
);
return
true
;
}
}
function replenishList(address[] accs, uint256[] amounts) public payable{
// To use this function, you should set gasLimit to at least gasEstimate + 50000
function
replenishList
(
address
[]
accs
,
uint256
[]
amounts
)
public
payable
{
require
(
accs
.
length
==
amounts
.
length
);
require
(
accs
.
length
==
amounts
.
length
);
uint256
i
=
0
;
uint256
i
=
0
;
if (contractHasEnoughEtherForThese(accs)){
while
(
i
<
accs
.
length
&&
gasleft
()
>
49999
)
for( ;i< accs.length; i++){
{
accs[i].transfer(get(beneficiaries, accs[i]) - accs[i].balance);
// we don't like overflow
}
if
(
accs
[
i
].
balance
+
amounts
[
i
]
<
accs
[
i
].
balance
)
continue
;
// Don't allow amounts which will make the balance go above the top up limit.
if
(
accs
[
i
].
balance
+
amounts
[
i
]
<
getTopUpLimit
(
UserBeneficiary
,
accs
[
i
]))
replenish
(
accs
[
i
],
amounts
[
i
]);
i
++
;
}
}
}
}
function replenishAll() public payable onlyDistributor returns(bool){
// Pay to as many as possible, until we run out of ether (or run through the list
if (contractHasEnoughEtherForAll()){
function
replenishAll
()
public
payable
onlyDistributorOrOwner
returns
(
bool
)
uint256 i = iterate_start(beneficiaries);
{
for (; iterate_valid(beneficiaries, i); i = iterate_next(beneficiaries, i)){
if
(
userIndex
.
length
==
0
)
var (cuenta, top) = iterate_get(beneficiaries, i);
return
false
;
if(top > cuenta.balance){
// The index may have shrunk under the stopmarker
cuenta.transfer(top - cuenta.balance);
if
(
allstopmarker
>=
userIndex
.
length
)
}
allstopmarker
=
0
;
}
// The distributor's topuplimit (for owner this is ignored)
return true;
uint256
distlimit
=
getTopUpLimit
(
msg
.
sender
);
uint256
i
=
allstopmarker
;
while
(
++
i
!=
allstopmarker
)
{
// If we get to the end of the list, loop back to the start of it
if
(
i
>=
userIndex
.
length
)
i
=
0
;
entry
=
userIndex
[
i
];
// make sure we skip deleted entries
if
(
entry
.
deleted
)
continue
;
// We only handle Beneficiaries
if
(
entry
.
userType
!=
UserBeneficiary
)
continue
;
//
if
(
msg
.
sender
==
owner
||
entry
.
topUpLimit
<=
distlimit
)
topuplimit
=
entry
.
topUpLimit
;
else
topuplimit
=
distlimit
;
// Does the user actually need to get topped up?
if
(
topuplimit
>
entry
.
key
.
balance
)
continue
;
// What's the difference?
uint256
amount
=
topuplimit
-
entry
.
key
.
balance
;
replenish
(
entry
.
key
,
amount
);
// Do we have enough ether to send?
if
(
address
(
this
).
balance
<
amount
)
continue
// Do the transfer and emit an event
address
payable
addrpayable
=
address
(
uint160
(
entry
.
key
))
addrpayable
.
transfer
(
amount
);
emit
replenished
(
msg
.
sender
,
entry
.
key
,
amount
);
}
}
return
fals
e;
return
tru
e
;
}
}
function contractHasEnoughEtherForThese(address[] accs) private view returns(bool){
function
getDistributorLimit
(
address
addr
)
public
view
returns
(
uint256
)
uint256 i = 0;
{
uint256 sum = 0;
return
getTopUpLimit
(
UserDistributor
,
addr
);
for( ;i< accs.length; i++){
}
uint256 top = get(beneficiaries, accs[i]);
if ( top > accs[i].balance){
function
getBeneficiaryLimit
(
address
addr
)
public
view
returns
(
uint256
)
sum += top - accs[i].balance;
{
}
return
getTopUpLimit
(
UserBeneficiary
,
addr
);
}
function
getBeneficiariesCount
()
public
view
returns
(
uint256
count
)
{
uint256
count
=
0
;
uint256
i
=
userIndex
.
length
;
while
(
i
--
>
0
)
{
if
(
userIndex
[
i
].
deleted
)
continue
if
(
userIndex
[
i
].
userType
==
UserBeneficiary
)
count
++
}
}
return sum < address(this).balance;
}
}
function
isBeneficiary
(
address
addr
)
public
view
returns
(
bool
)
function contractHasEnoughEtherForAll() private view returns(bool){
{
uint256 i = iterate_start(beneficiaries);
return
contains
(
UserBeneficiary
,
addr
);
uint256 sum = 0;
}
for (; iterate_valid(beneficiaries, i); i = iterate_next(beneficiaries, i)){
var (cuenta, top) = iterate_get(beneficiaries, i);
function
isDistributor
(
address
addr
)
public
view
returns
(
bool
)
if(top > cuenta.balance){
{
sum += top - cuenta.balance;
return
contains
(
UserDistributor
,
addr
);
}
}
function
insert
(
UserType
victimtype
,
address
addr
,
uint256
value
)
private
returns
(
bool
replaced
)
{
uint256
pos
=
addressToUserEntryIndex
[
addr
];
if
(
pos
>
0
&&
!
userIndex
[
pos
].
deleted
)
{
userIndex
[
pos
].
topUpLimit
=
value
;
self
.
position
[
addr
].
value
=
value
;
return
true
;
}
}
return sum < address(this).balance;
UserEntry
newEntry
=
UserEntry
({
}
addr
:
addr
,
userType
:
victimtype
,
function getDistributorLimit(address acc) public view returns(uint256){
topUpLimit
:
value
,
return get(distributors, acc);
deleted
:
false
}
});
// recycle deleted entries, if possible
function getBeneficiaryLimit(address acc) public view returns(uint256){
if
(
deletedEntries
.
length
>
0
)
return get(beneficiaries, acc);
{
}
pos
=
deletedEntries
[
deletedEntries
.
length
-
1
];
deletedEntries
.
pop
();
function getBeneficiariesCount() public view returns(uint256){
userIndex
[
pos
]
=
newEntry
;
return beneficiaries.size;
}
}
else
{
function isBeneficiary(address add) public view returns(bool){
pos
=
userIndex
.
length
;
return contains(beneficiaries, add);
userIndex
.
push
(
newEntry
);
}
}
addressToUserEntryIndex
[
addr
]
=
pos
;
function isDistributor(address add) public view returns(bool){
return contains(distributors, add);
}
// itmap
struct itmap{
mapping(address => IndexValue) data;
KeyFlag[] keys;
uint256 size;
}
struct IndexValue {
uint256 keyIndex;
uint256 value;
}
struct KeyFlag {
address key;
bool deleted;
}
function insert(itmap storage self, address key, uint256 value) private returns (bool replaced){
uint256 keyIndex = self.data[key].keyIndex;
self.data[key].value = value;
if (keyIndex > 0){
return true;
}else{
keyIndex = self.keys.length++;
self.data[key].keyIndex = keyIndex + 1;
self.keys[keyIndex].key = key;
self.size++;
return
false
;
return
false
;
}
}
}
function
remove
(
UserType
victimtype
,
address
addr
)
private
returns
(
bool
success
)
function size(itmap storage self) private view returns(uint256){
{
return self.size;
uint256
pos
=
addressToUserEntryIndex
[
addr
];
}
if
(
pos
==
0
)
return
false
;
function remove(itmap storage self, address key) private returns (bool success){
if
(
victimtype
!=
userIndex
[
pos
].
userType
)
uint256 keyIndex = self.data[key].keyIndex;
return
false
;
if (keyIndex == 0)
delete
addressToUserEntryIndex
[
addr
];
deletedEntries
.
push
(
pos
);
userIndex
[
pos
].
deleted
=
true
;
return
true
;
}
function
getEntry
(
address
addr
)
public
view
returns
(
UserEntry
)
{
uint256
pos
=
addressToUserEntryIndex
[
addr
];
// if no match, returns the 0th index, which is our "not found" entry
return
userIndex
[
pos
];
}
function
getUserType
(
address
addr
)
public
view
returns
(
UserType
)
{
uint256
pos
=
addressToUserEntryIndex
[
addr
];
// no match will give the 0th userIndex entry which has UserNotFound as UserType
return
userIndex
[
pos
].
userType
;
}
function
getTopUpLimit
(
UserType
victimtype
,
address
addr
)
public
returns
(
uint256
)
{
UserEntry
uentry
=
getEntry
(
addr
);
if
(
victimtype
==
uentry
.
userType
)
return
uentry
.
typUpLimit
;
return
0
;
}
function
contains
(
UserType
victimtype
,
address
addr
)
private
view
returns
(
bool
)
{
UserEntry
uentry
=
getEntry
(
addr
);
if
(
victimtype
==
uentry
.
userType
)
return
true
;
return
false
;
return
false
;
delete self.data[key];
}
self.keys[keyIndex - 1].deleted = true;
self.size --;
return true;
}
function modify(itmap storage self, address key, uint256 value) private returns (bool success){
uint256 keyIndex = self.data[key].keyIndex;
if (keyIndex == 0)
return false;
self.data[key].value = value;
return true;
}
function get(itmap storage self, address key) private view returns(uint){
return self.data[key].value;
}
function contains(itmap storage self, address key) private view returns (bool){
return self.data[key].keyIndex > 0;
}
function iterate_start(itmap storage self) private view returns (uint keyIndex){
return iterate_next(self, uint256(-1));
}
function iterate_valid(itmap storage self, uint256 keyIndex) private view returns (bool){
return keyIndex < self.keys.length;
}
function iterate_next(itmap storage self, uint256 keyIndex) private view returns (uint256 r_keyIndex){
keyIndex++;
while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
keyIndex++;
return keyIndex;
}
function iterate_get(itmap storage self, uint256 keyIndex) private view returns (address key, uint256 value){
key = self.keys[keyIndex].key;
value = self.data[key].value;
}
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment