erc20-transfer-authorization

Simple approval escrow for ERC20 spending
Log | Files | Refs

commit 7cb9e0baf0ab5db2dff71d2fb7279e2218e3cad0
parent e2dab1b72075bacf90026e1bb4f1e9406f0abc14
Author: lash <dev@holbrook.no>
Date:   Mon, 12 Dec 2022 08:43:12 +0000

Merge branch 'lash/port-tests'

Diffstat:
M.gitignore | 2++
Mpython/erc20_transfer_authorization/data/ERC20TransferAuthorization.bin | 4++--
Mpython/erc20_transfer_authorization/data/ERC20TransferAuthorization.json | 2+-
Mpython/erc20_transfer_authorization/transfer_authorization.py | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Apython/erc20_transfer_authorization/unittest/__init__.py | 1+
Apython/erc20_transfer_authorization/unittest/base.py | 45+++++++++++++++++++++++++++++++++++++++++++++
Mpython/requirements.txt | 5++---
Mpython/setup.cfg | 1+
Mpython/test_requirements.txt | 2+-
Dpython/tests/base_erc20transferauthorization.py | 49-------------------------------------------------
Dpython/tests/test_app.py | 165-------------------------------------------------------------------------------
Apython/tests/test_basic.py | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpython/tests/test_quorum.py | 790++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Apython/tests/test_request.py | 275+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpython/tests/test_transfer.py | 146++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msolidity/ERC20TransferAuthorization.sol | 151+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
16 files changed, 1379 insertions(+), 620 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -3,6 +3,8 @@ __pycache__ *.egg-info/ build/ dist/ +solidity/*.bin +solidity/*.json gmon.out .idea venv diff --git a/python/erc20_transfer_authorization/data/ERC20TransferAuthorization.bin b/python/erc20_transfer_authorization/data/ERC20TransferAuthorization.bin @@ -1 +1 @@ -60806040523480156200001157600080fd5b5033600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160068190555060016007819055506200007d33620000a2640100000000026401000000009004565b506200009b6001600062000282640100000000026401000000009004565b5062000539565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161462000137576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200012e906200039a565b60405180910390fd5b60001515600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514620001cd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001c490620003bc565b60405180910390fd5b6001600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600b60008154809291906200023a906200046a565b91905055507f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f24826040516200027091906200037d565b60405180910390a1600b549050919050565b6000600b548311156200029457600080fd5b60008311620002a257600080fd5b600b54821115620002b257600080fd5b8260098190555081600a819055507f9f40cfd22fe91777c78f252bd21a710f3fb007dc2f321876891e7644ba0ae175600954600a54600b54604051620002fb93929190620003de565b60405180910390a16001905092915050565b62000318816200042c565b82525050565b60006200032d600a836200041b565b91506200033a82620004e7565b602082019050919050565b600062000354600c836200041b565b9150620003618262000510565b602082019050919050565b620003778162000460565b82525050565b60006020820190506200039460008301846200030d565b92915050565b60006020820190508181036000830152620003b5816200031e565b9050919050565b60006020820190508181036000830152620003d78162000345565b9050919050565b6000606082019050620003f560008301866200036c565b6200040460208301856200036c565b6200041360408301846200036c565b949350505050565b600082825260208201905092915050565b6000620004398262000440565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000620004778262000460565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415620004ad57620004ac620004b8565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4552525f41434345535300000000000000000000000000000000000000000000600082015250565b7f4552525f4e4f54464f554e440000000000000000000000000000000000000000600082015250565b61239380620005496000396000f3fe608060405234801561001057600080fd5b506004361061015f576000357c010000000000000000000000000000000000000000000000000000000090048063736c0d5b116100d5578063bac911ac11610099578063bac911ac146103ee578063e3064a771461041e578063e9f9d28b1461044e578063eb12d61e1461047e578063ed71262a146104ae578063fba00cbd146104de5761015f565b8063736c0d5b1461031a578063755fd58f1461034a5780637ca548c61461037a57806381d12c58146103985780638da5cb5b146103d05761015f565b80633a9ddc80116101275780633a9ddc801461021e5780634797569a1461024e57806357ec347a1461027e57806358d71f821461029c578063614f427f146102cc5780636e417c69146102ea5761015f565b806302d947ef1461016457806306661abd1461019457806308ae6377146101b25780630e316ab7146101d05780631703a01814610200575b600080fd5b61017e60048036038101906101799190611bd0565b61050e565b60405161018b9190611e35565b60405180910390f35b61019c61053d565b6040516101a99190611e1a565b60405180910390f35b6101ba610543565b6040516101c79190611f10565b60405180910390f35b6101ea60048036038101906101e59190611adf565b61056e565b6040516101f79190611f10565b60405180910390f35b6102086107cf565b6040516102159190611f10565b60405180910390f35b61023860048036038101906102339190611b6b565b6107d5565b6040516102459190611f10565b60405180910390f35b61026860048036038101906102639190611ba7565b610806565b6040516102759190611f10565b60405180910390f35b610286610aaa565b6040516102939190611f10565b60405180910390f35b6102b660048036038101906102b19190611ba7565b610ac0565b6040516102c39190611f10565b60405180910390f35b6102d4610d45565b6040516102e19190611f10565b60405180910390f35b61030460048036038101906102ff9190611ba7565b610d4b565b6040516103119190611dff565b60405180910390f35b610334600480360381019061032f9190611adf565b61114a565b6040516103419190611dff565b60405180910390f35b610364600480360381019061035f9190611b6b565b61116a565b6040516103719190611f10565b60405180910390f35b61038261119b565b60405161038f9190611f10565b60405180910390f35b6103b260048036038101906103ad9190611ba7565b6111a1565b6040516103c799989796959493929190611f2b565b60405180910390f35b6103d861125c565b6040516103e59190611dad565b60405180910390f35b61040860048036038101906104039190611ba7565b611282565b6040516104159190611f10565b60405180910390f35b61043860048036038101906104339190611c0c565b611311565b6040516104459190611dff565b60405180910390f35b61046860048036038101906104639190611ba7565b611397565b6040516104759190611dff565b60405180910390f35b61049860048036038101906104939190611adf565b6115d0565b6040516104a59190611f10565b60405180910390f35b6104c860048036038101906104c39190611b08565b6117a6565b6040516104d59190611f10565b60405180910390f35b6104f860048036038101906104f39190611c0c565b6119f0565b6040516105059190611dad565b60405180910390f35b60006020528160005260406000206020528060005260406000206000915091509054906101000a900460000b81565b60085481565b600080600754600654610556919061203f565b1415610565576000905061056b565b60075490505b90565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806105f757508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b610636576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161062d90611e70565b60405180910390fd5b60011515600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146106c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c090611e90565b60405180910390fd5b600954600b541180156106df5750600a54600b54115b61071e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161071590611e50565b60405180910390fd5b6000600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600b60008154809291906107899061214e565b91905055507f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b826040516107bd9190611dad565b60405180910390a1600b549050919050565b60095481565b600560205281600052604060002081815481106107f157600080fd5b90600052602060002001600091509150505481565b6000600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610894576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088b90611e70565b60405180910390fd5b600080600084815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460000b60000b14610936576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161092d90611eb0565b60405180910390fd5b600060036000848152602001908152602001600020905060018160080160009054906101000a900460000b60000b1461096e57600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000808360000154815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908360000b60ff1602179055506001600082600001548152602001908152602001600020339080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806006016000815480929190610a8a906121c1565b9190505550610a9c8160000154611397565b508060060154915050919050565b60006001600654610abb919061203f565b905090565b6000600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610b4e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4590611e70565b60405180910390fd5b600080600084815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460000b60000b14610bf0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610be790611eb0565b60405180910390fd5b600060036000848152602001908152602001600020905060018160080160009054906101000a900460000b60000b14610c2857600080fd5b60016000808360000154815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908360000b60ff1602179055506001600082600001548152602001908152602001600020339080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806005016000815480929190610d25906121c1565b9190505550610d378160000154611397565b508060050154915050919050565b600a5481565b6000806003600084815260200190815260200160002090506000816000015411610daa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da190611ed0565b60405180910390fd5b60028160080160009054906101000a900460000b60000b14610e01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610df890611ef0565b60405180910390fd5b60008160080160006101000a81548160ff021916908360000b60ff1602179055506000808260030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168360010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168460020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168560040154604051602401610ec193929190611dc8565b6040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610f4b9190611d96565b6000604051808303816000865af19150503d8060008114610f88576040519150601f19603f3d011682016040523d82523d6000602084013e610f8d565b606091505b5091509150610f9f8360000154611a3e565b50438360070181905550600460008460010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002083600001549080600181540180825580915050600190039060005260206000200160009091909190915055600560008460020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020836000015490806001815401808255809150506001900390600052602060002001600090919091909150558115611107577fbcf6a68a2f901be4a23a41b53acd7697893a7e34def4e28acba584da75283b67856040516110fa9190611f10565b60405180910390a161113f565b7fdab20a0fcd702cf875c2d715d5c3fc99af66a716c94b3405408c94b7311c99eb856040516111369190611f10565b60405180910390a15b819350505050919050565b600c6020528060005260406000206000915054906101000a900460ff1681565b6004602052816000526040600020818154811061118657600080fd5b90600052602060002001600091509150505481565b600b5481565b60036020528060005260406000206000915090508060000154908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060040154908060050154908060060154908060070154908060080160009054906101000a900460000b905089565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600060075490505b60065481101561130557600060036000838152602001908152602001600020905060008160080160009054906101000a900460000b60000b13156112f157848314156112e2578060000154935050505061130c565b82806112ed906121c1565b9350505b5080806112fd906121c1565b91505061128d565b5060009150505b919050565b6000600b5483111561132257600080fd5b6000831161132f57600080fd5b600b5482111561133e57600080fd5b8260098190555081600a819055507f9f40cfd22fe91777c78f252bd21a710f3fb007dc2f321876891e7644ba0ae175600954600a54600b5460405161138593929190611fe1565b60405180910390a16001905092915050565b600080600060036000858152602001908152602001600020905060018160080160009054906101000a900460000b60000b12156113d85781925050506115cb565b6009548160050154106114555760028160080160006101000a81548160ff021916908360000b60ff16021790555080600001547f36ea04725f8aa40ee603224671681b753f9cba3cb5f67c5a0e24a3b39900c06582600501548360060154604051611444929190611fb8565b60405180910390a2600191506115c5565b6000600a5411801561146d5750600a54816006015410155b1561150f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8160080160006101000a81548160ff021916908360000b60ff1602179055506114bf8160000154611a3e565b5080600001547f1ad80b2541a1f52bdc838332d7c23606116a1188a8cbbc4c0948b4b56ce51d14826005015483600601546040516114fe929190611fb8565b60405180910390a2600191506115c4565b6009548160060154600b54611524919061203f565b10156115c3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8160080160006101000a81548160ff021916908360000b60ff1602179055506115778160000154611a3e565b5080600001547f3d61d434b895790b08f040c45261fce3b3bec596278b3a0f25dd9f741d0ba469826005015483600601546040516115b6929190611fb8565b60405180910390a2600191505b5b5b81925050505b919050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611662576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161165990611e70565b60405180910390fd5b60001515600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146116f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116ec90611e90565b60405180910390fd5b6001600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600b6000815480929190611760906121c1565b91905055507f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f24826040516117949190611dad565b60405180910390a1600b549050919050565b60008060036000600654815260200190815260200160002090506006548160000181905550848160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550858160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838160030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082816004018190555060018160080160006101000a81548160ff021916908360000b60ff160217905550600860008154809291906118d190612178565b9190505550600660008154809291906118e9906121c1565b91905055508060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168260010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fb609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c846004015485600001546040516119d8929190611fb8565b60405180910390a48060000154915050949350505050565b60016020528160005260406000208181548110611a0c57600080fd5b906000526020600020016000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060086000815480929190611a5390612105565b919050555060006008541315611aa157600754821415611a9c576000611a796000611282565b90506000811415611a9257600654600781905550611a9a565b806007819055505b505b611aab565b6006546007819055505b6007549050919050565b600081359050611ac48161232f565b92915050565b600081359050611ad981612346565b92915050565b600060208284031215611af157600080fd5b6000611aff84828501611ab5565b91505092915050565b60008060008060808587031215611b1e57600080fd5b6000611b2c87828801611ab5565b9450506020611b3d87828801611ab5565b9350506040611b4e87828801611ab5565b9250506060611b5f87828801611aca565b91505092959194509250565b60008060408385031215611b7e57600080fd5b6000611b8c85828601611ab5565b9250506020611b9d85828601611aca565b9150509250929050565b600060208284031215611bb957600080fd5b6000611bc784828501611aca565b91505092915050565b60008060408385031215611be357600080fd5b6000611bf185828601611aca565b9250506020611c0285828601611ab5565b9150509250929050565b60008060408385031215611c1f57600080fd5b6000611c2d85828601611aca565b9250506020611c3e85828601611aca565b9150509250929050565b611c5181612073565b82525050565b611c6081612085565b82525050565b6000611c7182612018565b611c7b8185612023565b9350611c8b8185602086016120d2565b80840191505092915050565b611ca081612091565b82525050565b611caf8161209b565b82525050565b6000611cc2601a8361202e565b9150611ccd82612239565b602082019050919050565b6000611ce5600a8361202e565b9150611cf082612262565b602082019050919050565b6000611d08600c8361202e565b9150611d138261228b565b602082019050919050565b6000611d2b60108361202e565b9150611d36826122b4565b602082019050919050565b6000611d4e60138361202e565b9150611d59826122dd565b602082019050919050565b6000611d7160108361202e565b9150611d7c82612306565b602082019050919050565b611d90816120c8565b82525050565b6000611da28284611c66565b915081905092915050565b6000602082019050611dc26000830184611c48565b92915050565b6000606082019050611ddd6000830186611c48565b611dea6020830185611c48565b611df76040830184611d87565b949350505050565b6000602082019050611e146000830184611c57565b92915050565b6000602082019050611e2f6000830184611c97565b92915050565b6000602082019050611e4a6000830184611ca6565b92915050565b60006020820190508181036000830152611e6981611cb5565b9050919050565b60006020820190508181036000830152611e8981611cd8565b9050919050565b60006020820190508181036000830152611ea981611cfb565b9050919050565b60006020820190508181036000830152611ec981611d1e565b9050919050565b60006020820190508181036000830152611ee981611d41565b9050919050565b60006020820190508181036000830152611f0981611d64565b9050919050565b6000602082019050611f256000830184611d87565b92915050565b600061012082019050611f41600083018c611d87565b611f4e602083018b611c48565b611f5b604083018a611c48565b611f686060830189611c48565b611f756080830188611d87565b611f8260a0830187611d87565b611f8f60c0830186611d87565b611f9c60e0830185611d87565b611faa610100830184611ca6565b9a9950505050505050505050565b6000604082019050611fcd6000830185611d87565b611fda6020830184611d87565b9392505050565b6000606082019050611ff66000830186611d87565b6120036020830185611d87565b6120106040830184611d87565b949350505050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b600061204a826120c8565b9150612055836120c8565b9250828210156120685761206761220a565b5b828203905092915050565b600061207e826120a8565b9050919050565b60008115159050919050565b6000819050919050565b60008160000b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b838110156120f05780820151818401526020810190506120d5565b838111156120ff576000848401525b50505050565b600061211082612091565b91507f80000000000000000000000000000000000000000000000000000000000000008214156121435761214261220a565b5b600182039050919050565b6000612159826120c8565b9150600082141561216d5761216c61220a565b5b600182039050919050565b600061218382612091565b91507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156121b6576121b561220a565b5b600182019050919050565b60006121cc826120c8565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156121ff576121fe61220a565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4552525f5245445543455f5448524553484f4c445f4649525354000000000000600082015250565b7f4552525f41434345535300000000000000000000000000000000000000000000600082015250565b7f4552525f4e4f54464f554e440000000000000000000000000000000000000000600082015250565b7f4552525f414c5245414459564f54454400000000000000000000000000000000600082015250565b7f4552525f494e56414c49445f5245515545535400000000000000000000000000600082015250565b7f4552525f4e4f545f454e444f5253454400000000000000000000000000000000600082015250565b61233881612073565b811461234357600080fd5b50565b61234f816120c8565b811461235a57600080fd5b5056fea2646970667358221220db3bb04d4609a28abcde3217e8cc5e6a6573c3ee648d8e7f88e10daa0a5f7ec264736f6c63430008020033 -\ No newline at end of file +60806040523480156200001157600080fd5b5033600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600260146101000a81548163ffffffff021916908363ffffffff1602179055506001600260186101000a81548163ffffffff021916908363ffffffff160217905550620000b133620000d6640100000000026401000000009004565b50620000cf60016000620002ef640100000000026401000000009004565b50620005e7565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146200016b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001629062000481565b60405180910390fd5b60001515600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151462000201576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001f890620004f3565b60405180910390fd5b6001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506003600881819054906101000a900463ffffffff16809291906200027d9062000554565b91906101000a81548163ffffffff021916908363ffffffff160217905550507f6ff3aa2ea7b53070f6d9d07a445d338d89e8edef44250ffa8be19f53910d4a2e82604051620002cd9190620005ca565b60405180910390a1600360089054906101000a900463ffffffff169050919050565b6000600360089054906101000a900463ffffffff1663ffffffff168363ffffffff1611156200031d57600080fd5b60008363ffffffff16116200033157600080fd5b600360089054906101000a900463ffffffff1663ffffffff168263ffffffff1611156200035d57600080fd5b82600360006101000a81548163ffffffff021916908363ffffffff16021790555081600360046101000a81548163ffffffff021916908363ffffffff160217905550600360089054906101000a900463ffffffff1663ffffffff16600360049054906101000a900463ffffffff1663ffffffff16600360009054906101000a900463ffffffff1663ffffffff167fe77378573ac0f86c7fa8dd116b1fa17cf9c328a09a0c56a2c42d786103ac5bc360405160405180910390a46001905092915050565b600082825260208201905092915050565b7f4552525f41434345535300000000000000000000000000000000000000000000600082015250565b600062000469600a8362000420565b9150620004768262000431565b602082019050919050565b600060208201905081810360008301526200049c816200045a565b9050919050565b7f4552525f4e4f54464f554e440000000000000000000000000000000000000000600082015250565b6000620004db600c8362000420565b9150620004e882620004a3565b602082019050919050565b600060208201905081810360008301526200050e81620004cc565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600063ffffffff82169050919050565b6000620005618262000544565b915063ffffffff82036200057a576200057962000515565b5b600182019050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620005b28262000585565b9050919050565b620005c481620005a5565b82525050565b6000602082019050620005e16000830184620005b9565b92915050565b61284580620005f76000396000f3fe608060405234801561001057600080fd5b5060043610610154576000357c010000000000000000000000000000000000000000000000000000000090048063614f427f116100d55780637ca548c6116100995780637ca548c6146103d65780637f4dbec1146103f45780638da5cb5b14610424578063da2824a814610442578063ed71262a14610472578063fdd33d68146104a257610154565b8063614f427f146102f8578063655c013114610316578063669cd53b1461034657806366a5267214610376578063736c0d5b146103a657610154565b80631703a0181161011c5780631703a0181461022c5780632b29ba231461024a578063476ce3ad1461027a5780635356dddc146102aa57806357ec347a146102da57610154565b806306661abd1461015957806308ae6377146101775780630ab469b0146101955780630ac94000146101cc57806310858c67146101fc575b600080fd5b6101616104d2565b60405161016e9190612044565b60405180910390f35b61017f6104e8565b60405161018c9190612044565b60405180910390f35b6101af60048036038101906101aa9190612090565b6105df565b6040516101c3989796959493929190612133565b60405180910390f35b6101e660048036038101906101e19190612090565b6106c4565b6040516101f391906121cc565b60405180910390f35b61021660048036038101906102119190612090565b610a12565b6040516102239190612044565b60405180910390f35b610234610b37565b6040516102419190612044565b60405180910390f35b610264600480360381019061025f9190612213565b610b4d565b60405161027191906121cc565b60405180910390f35b610294600480360381019061028f9190612090565b610ba3565b6040516102a19190612044565b60405180910390f35b6102c460048036038101906102bf9190612213565b610e2d565b6040516102d19190612044565b60405180910390f35b6102e26110e9565b6040516102ef9190612044565b60405180910390f35b61030061110f565b60405161030d9190612044565b60405180910390f35b610330600480360381019061032b9190612240565b611125565b60405161033d919061229c565b60405180910390f35b610360600480360381019061035b91906122b7565b611154565b60405161036d91906121cc565b60405180910390f35b610390600480360381019061038b9190612323565b611282565b60405161039d9190612350565b60405180910390f35b6103c060048036038101906103bb9190612213565b6112c1565b6040516103cd91906121cc565b60405180910390f35b6103de6112e1565b6040516103eb9190612044565b60405180910390f35b61040e60048036038101906104099190612090565b6112f7565b60405161041b9190612044565b60405180910390f35b61042c6115a0565b6040516104399190612350565b60405180910390f35b61045c60048036038101906104579190612213565b6115c6565b6040516104699190612044565b60405180910390f35b61048c6004803603810190610487919061236b565b6117d5565b6040516104999190612044565b60405180910390f35b6104bc60048036038101906104b79190612090565b611ad4565b6040516104c991906121cc565b60405180910390f35b6002601c9054906101000a900463ffffffff1681565b600080600260189054906101000a900463ffffffff16600260149054906101000a900463ffffffff1661051b9190612401565b63ffffffff16036105c6576001600260149054906101000a900463ffffffff1663ffffffff160361054f57600090506105dc565b600060016000600260189054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001908152602001600020905060008160040160009054906101000a900460ff1660ff1611156105bc57600260189054906101000a900463ffffffff169150506105dc565b60009150506105dc565b600260189054906101000a900463ffffffff1690505b90565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060030160149054906101000a900463ffffffff16908060030160189054906101000a900463ffffffff169080600301601c9054906101000a900463ffffffff16908060040160009054906101000a900460ff16905088565b600080600160008463ffffffff1663ffffffff168152602001908152602001600020905060008160030160149054906101000a900463ffffffff1663ffffffff1611610745576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161073c90612496565b60405180910390fd5b6003600b8260040160009054906101000a900460ff161660ff161461079f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161079690612502565b60405180910390fd5b6107bc8160030160149054906101000a900463ffffffff16611e99565b5060088160040160008282829054906101000a900460ff161792506101000a81548160ff021916908360ff1602179055506000808260030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168360010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168460020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16856000015460405160240161088c93929190612522565b6040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161091691906125ca565b6000604051808303816000865af19150503d8060008114610953576040519150601f19603f3d011682016040523d82523d6000602084013e610958565b606091505b5091509150811561099f577fa80d5e3445b11533e6c584c506299aa14168f2ad1fe65b0266e7d622c5461c30856040516109929190612044565b60405180910390a1610a07565b60108360040160008282829054906101000a900460ff161792506101000a81548160ff021916908360ff1602179055507f8f15e24c8a8ecaa6b067f0cd2c220532e6496f53b2ac3263129d1496cb6f192e856040516109fe9190612044565b60405180910390a15b819350505050919050565b600080600260149054906101000a900463ffffffff1663ffffffff16600260189054906101000a900463ffffffff1663ffffffff1603610a56576000915050610b32565b6000600260189054906101000a900463ffffffff1690505b600260149054906101000a900463ffffffff1663ffffffff168163ffffffff161015610b2b576000600160008363ffffffff1663ffffffff1681526020019081526020016000209050600160078260040160009054906101000a900460ff161660ff1603610b17578463ffffffff168363ffffffff1603610b08578060030160149054906101000a900463ffffffff169350505050610b32565b8280610b13906125e1565b9350505b508080610b23906125e1565b915050610a6e565b5060009150505b919050565b600360009054906101000a900463ffffffff1681565b6000600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff169050919050565b6000600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610c31576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c2890612659565b60405180910390fd5b60008060008463ffffffff1663ffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460000b60000b14610cdf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cd6906126c5565b60405180910390fd5b6000600160008463ffffffff1663ffffffff168152602001908152602001600020905060018160040160009054906101000a900460ff1660ff1614610d2357600080fd5b60016000808360030160149054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908360000b60ff16021790555080600301601881819054906101000a900463ffffffff1680929190610dd3906125e1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050610e0f8160030160149054906101000a900463ffffffff16611ad4565b508060030160189054906101000a900463ffffffff16915050919050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ebf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb690612659565b60405180910390fd5b60011515600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514610f52576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f4990612731565b60405180910390fd5b600360009054906101000a900463ffffffff1663ffffffff16600360089054906101000a900463ffffffff1663ffffffff16118015610fc05750600360049054906101000a900463ffffffff1663ffffffff16600360089054906101000a900463ffffffff1663ffffffff16115b610fff576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ff69061279d565b60405180910390fd5b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506003600881819054906101000a900463ffffffff1680929190611079906127bd565b91906101000a81548163ffffffff021916908363ffffffff160217905550507f86e5bbceda94081c32220d685f37cc4e3ea7bb0be2dfbf0cb703579505a5390e826040516110c79190612350565b60405180910390a1600360089054906101000a900463ffffffff169050919050565b60006001600260149054906101000a900463ffffffff1661110a9190612401565b905090565b600360049054906101000a900463ffffffff1681565b60006020528160005260406000206020528060005260406000206000915091509054906101000a900460000b81565b6000600360089054906101000a900463ffffffff1663ffffffff168363ffffffff16111561118157600080fd5b60008363ffffffff161161119457600080fd5b600360089054906101000a900463ffffffff1663ffffffff168263ffffffff1611156111bf57600080fd5b82600360006101000a81548163ffffffff021916908363ffffffff16021790555081600360046101000a81548163ffffffff021916908363ffffffff160217905550600360089054906101000a900463ffffffff1663ffffffff16600360049054906101000a900463ffffffff1663ffffffff16600360009054906101000a900463ffffffff1663ffffffff167fe77378573ac0f86c7fa8dd116b1fa17cf9c328a09a0c56a2c42d786103ac5bc360405160405180910390a46001905092915050565b6005818154811061129257600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60046020528060005260406000206000915054906101000a900460ff1681565b600360089054906101000a900463ffffffff1681565b6000600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16611385576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161137c90612659565b60405180910390fd5b60008060008463ffffffff1663ffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460000b60000b14611433576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161142a906126c5565b60405180910390fd5b6000600160008463ffffffff1663ffffffff168152602001908152602001600020905060018160040160009054906101000a900460ff1660ff161461147757600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6000808360030160149054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908360000b60ff16021790555080600301601c81819054906101000a900463ffffffff1680929190611546906125e1565b91906101000a81548163ffffffff021916908363ffffffff160217905550506115828160030160149054906101000a900463ffffffff16611ad4565b5080600301601c9054906101000a900463ffffffff16915050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611658576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161164f90612659565b60405180910390fd5b60001515600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146116eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116e290612731565b60405180910390fd5b6001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506003600881819054906101000a900463ffffffff1680929190611765906125e1565b91906101000a81548163ffffffff021916908363ffffffff160217905550507f6ff3aa2ea7b53070f6d9d07a445d338d89e8edef44250ffa8be19f53910d4a2e826040516117b39190612350565b60405180910390a1600360089054906101000a900463ffffffff169050919050565b60008060016000600260149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020019081526020016000209050600260149054906101000a900463ffffffff168160030160146101000a81548163ffffffff021916908363ffffffff160217905550848160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550858160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838160030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082816000018190555060018160040160006101000a81548160ff021916908360ff1602179055506002601c81819054906101000a900463ffffffff1680929190611952906125e1565b91906101000a81548163ffffffff021916908363ffffffff160217905550506002601481819054906101000a900463ffffffff1680929190611993906125e1565b91906101000a81548163ffffffff021916908363ffffffff160217905550508060030160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168260010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f3ba7a259b57ae3d653287762394374c856528591f691ecd6494021fdcea00f2484600001548560030160149054906101000a900463ffffffff16604051611aac9291906127e6565b60405180910390a48060030160149054906101000a900463ffffffff16915050949350505050565b6000806000600160008563ffffffff1663ffffffff168152602001908152602001600020905060018160040160009054906101000a900460ff1660ff161080611b345750600060068260040160009054906101000a900460ff161660ff16115b15611b43578192505050611e94565b600360009054906101000a900463ffffffff1663ffffffff168160030160189054906101000a900463ffffffff1663ffffffff1610611c325760028160040160008282829054906101000a900460ff161792506101000a81548160ff021916908360ff16021790555080600301601c9054906101000a900463ffffffff1663ffffffff168160030160189054906101000a900463ffffffff1663ffffffff168260030160149054906101000a900463ffffffff1663ffffffff167f7a17acca3819c1012204b2d019c0ee13bb7e5cf151111030329219582ba83f3160405160405180910390a460019150611e8e565b6000600360049054906101000a900463ffffffff1663ffffffff16118015611c8c5750600360049054906101000a900463ffffffff1663ffffffff1681600301601c9054906101000a900463ffffffff1663ffffffff1610155b15611d655760048160040160008282829054906101000a900460ff161792506101000a81548160ff021916908360ff160217905550611cde8160030160149054906101000a900463ffffffff16611e99565b5080600301601c9054906101000a900463ffffffff1663ffffffff168160030160189054906101000a900463ffffffff1663ffffffff168260030160149054906101000a900463ffffffff1663ffffffff167f4bdd2d0e6caefafbf833eda0d32f2dcb0e7a621e3e7b87b2a17c12090edbc0d660405160405180910390a460019150611e8d565b600360009054906101000a900463ffffffff1663ffffffff1681600301601c9054906101000a900463ffffffff16600360089054906101000a900463ffffffff16611db09190612401565b63ffffffff161015611e8c5760048160040160008282829054906101000a900460ff161792506101000a81548160ff021916908360ff160217905550611e098160030160149054906101000a900463ffffffff16611e99565b5080600301601c9054906101000a900463ffffffff1663ffffffff168160030160189054906101000a900463ffffffff1663ffffffff168260030160149054906101000a900463ffffffff1663ffffffff167f3c478de516aa199022ddcd594a6e3b7f03a3e23bfcafd6143adceb687a8e38ea60405160405180910390a4600191505b5b5b81925050505b919050565b60006002601c81819054906101000a900463ffffffff1680929190611ebd906127bd565b91906101000a81548163ffffffff021916908363ffffffff1602179055505060006002601c9054906101000a900463ffffffff1663ffffffff161115611f9f57600260189054906101000a900463ffffffff1663ffffffff168263ffffffff1603611f9a576000611f2e6000610a12565b905060008163ffffffff1603611f7657600260149054906101000a900463ffffffff16600260186101000a81548163ffffffff021916908363ffffffff160217905550611f98565b80600260186101000a81548163ffffffff021916908363ffffffff1602179055505b505b61200b565b600260149054906101000a900463ffffffff1663ffffffff16600260189054906101000a900463ffffffff1663ffffffff161461200a57600260149054906101000a900463ffffffff16600260186101000a81548163ffffffff021916908363ffffffff1602179055505b5b600260189054906101000a900463ffffffff169050919050565b600063ffffffff82169050919050565b61203e81612025565b82525050565b60006020820190506120596000830184612035565b92915050565b600080fd5b61206d81612025565b811461207857600080fd5b50565b60008135905061208a81612064565b92915050565b6000602082840312156120a6576120a561205f565b5b60006120b48482850161207b565b91505092915050565b6000819050919050565b6120d0816120bd565b82525050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000612101826120d6565b9050919050565b612111816120f6565b82525050565b600060ff82169050919050565b61212d81612117565b82525050565b600061010082019050612149600083018b6120c7565b612156602083018a612108565b6121636040830189612108565b6121706060830188612108565b61217d6080830187612035565b61218a60a0830186612035565b61219760c0830185612035565b6121a460e0830184612124565b9998505050505050505050565b60008115159050919050565b6121c6816121b1565b82525050565b60006020820190506121e160008301846121bd565b92915050565b6121f0816120f6565b81146121fb57600080fd5b50565b60008135905061220d816121e7565b92915050565b6000602082840312156122295761222861205f565b5b6000612237848285016121fe565b91505092915050565b600080604083850312156122575761225661205f565b5b60006122658582860161207b565b9250506020612276858286016121fe565b9150509250929050565b60008160000b9050919050565b61229681612280565b82525050565b60006020820190506122b1600083018461228d565b92915050565b600080604083850312156122ce576122cd61205f565b5b60006122dc8582860161207b565b92505060206122ed8582860161207b565b9150509250929050565b612300816120bd565b811461230b57600080fd5b50565b60008135905061231d816122f7565b92915050565b6000602082840312156123395761233861205f565b5b60006123478482850161230e565b91505092915050565b60006020820190506123656000830184612108565b92915050565b600080600080608085870312156123855761238461205f565b5b6000612393878288016121fe565b94505060206123a4878288016121fe565b93505060406123b5878288016121fe565b92505060606123c68782880161230e565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061240c82612025565b915061241783612025565b9250828203905063ffffffff811115612433576124326123d2565b5b92915050565b600082825260208201905092915050565b7f4552525f494e56414c49445f5245515545535400000000000000000000000000600082015250565b6000612480601383612439565b915061248b8261244a565b602082019050919050565b600060208201905081810360008301526124af81612473565b9050919050565b7f4552525f4e4f545f454e444f5253454400000000000000000000000000000000600082015250565b60006124ec601083612439565b91506124f7826124b6565b602082019050919050565b6000602082019050818103600083015261251b816124df565b9050919050565b60006060820190506125376000830186612108565b6125446020830185612108565b61255160408301846120c7565b949350505050565b600081519050919050565b600081905092915050565b60005b8381101561258d578082015181840152602081019050612572565b60008484015250505050565b60006125a482612559565b6125ae8185612564565b93506125be81856020860161256f565b80840191505092915050565b60006125d68284612599565b915081905092915050565b60006125ec82612025565b915063ffffffff8203612602576126016123d2565b5b600182019050919050565b7f4552525f41434345535300000000000000000000000000000000000000000000600082015250565b6000612643600a83612439565b915061264e8261260d565b602082019050919050565b6000602082019050818103600083015261267281612636565b9050919050565b7f4552525f414c5245414459564f54454400000000000000000000000000000000600082015250565b60006126af601083612439565b91506126ba82612679565b602082019050919050565b600060208201905081810360008301526126de816126a2565b9050919050565b7f4552525f4e4f54464f554e440000000000000000000000000000000000000000600082015250565b600061271b600c83612439565b9150612726826126e5565b602082019050919050565b6000602082019050818103600083015261274a8161270e565b9050919050565b7f4552525f5245445543455f5448524553484f4c445f4649525354000000000000600082015250565b6000612787601a83612439565b915061279282612751565b602082019050919050565b600060208201905081810360008301526127b68161277a565b9050919050565b60006127c882612025565b9150600082036127db576127da6123d2565b5b600182039050919050565b60006040820190506127fb60008301856120c7565b6128086020830184612035565b939250505056fea26469706673582212202cc5839cf3734f7f04d95e61219fc5e69a1a116202984ee674eb387bbaaa9b1764736f6c63430008110033 +\ No newline at end of file diff --git a/python/erc20_transfer_authorization/data/ERC20TransferAuthorization.json b/python/erc20_transfer_authorization/data/ERC20TransferAuthorization.json @@ -1 +1 @@ -[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_serial","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_yays","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_nays","type":"uint256"}],"name":"Approved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"Executed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"NewRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_quorum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_vetoThreshold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_signerCount","type":"uint256"}],"name":"QuorumSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_serial","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_yays","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_nays","type":"uint256"}],"name":"Rejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_signer","type":"address"}],"name":"SignerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_signer","type":"address"}],"name":"SignerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"TransferFail","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_serial","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_yays","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_nays","type":"uint256"}],"name":"Vetoed","type":"event"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"addSigner","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"checkResult","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"createRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"executeRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"getSerialAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSerial","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"nay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextSerial","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quorum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"removeSigner","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"requestRecipientIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"requestSenderIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"requests","outputs":[{"internalType":"uint256","name":"serial","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"yay","type":"uint256"},{"internalType":"uint256","name":"nay","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"int8","name":"result","type":"int8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_quorum","type":"uint256"},{"internalType":"uint256","name":"_vetoThreshold","type":"uint256"}],"name":"setThresholds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signerCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"signers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vetoThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"vote","outputs":[{"internalType":"int8","name":"","type":"int8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"voters","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"yay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}] +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"_serial","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_yays","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_nays","type":"uint32"}],"name":"Approved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"_serial","type":"uint32"}],"name":"Executed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"_serial","type":"uint32"}],"name":"NewRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"_quorum","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_vetoThreshold","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_signerCount","type":"uint32"}],"name":"QuorumSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"_serial","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_yays","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_nays","type":"uint32"}],"name":"Rejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"_serial","type":"uint32"}],"name":"TransferFail","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"_serial","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_yays","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"_nays","type":"uint32"}],"name":"Vetoed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_signer","type":"address"}],"name":"WriterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_signer","type":"address"}],"name":"WriterRemoved","type":"event"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"addWriter","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_serial","type":"uint32"}],"name":"checkResult","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"createRequest","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_serial","type":"uint32"}],"name":"executeRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_idx","type":"uint32"}],"name":"getSerialAt","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"isWriter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSerial","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_serial","type":"uint32"}],"name":"nay","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextSerial","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quorum","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"removeWriter","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"requests","outputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"serial","type":"uint32"},{"internalType":"uint32","name":"yay","type":"uint32"},{"internalType":"uint32","name":"nay","type":"uint32"},{"internalType":"uint8","name":"result","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_quorum","type":"uint32"},{"internalType":"uint32","name":"_vetoThreshold","type":"uint32"}],"name":"setThresholds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signerCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"signers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vetoThreshold","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"address","name":"","type":"address"}],"name":"vote","outputs":[{"internalType":"int8","name":"","type":"int8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"writers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_serial","type":"uint32"}],"name":"yay","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"}] diff --git a/python/erc20_transfer_authorization/transfer_authorization.py b/python/erc20_transfer_authorization/transfer_authorization.py @@ -32,11 +32,88 @@ moddir = os.path.dirname(__file__) datadir = os.path.join(moddir, 'data') +class Request: + + def __init__(self, sender, recipient, value, token): + self.sender = sender + self.recipient = recipient + self.value = value + self.token = token + self.serial = 0 + self.yay = 0 + self.nay = 0 + self.result = 0 + + + @classmethod + def create(cls, sender, recipient, value, token, *args): + o = Request(sender, recipient, value, token) + if len(args) > 0: + o.serial = args[0] + o.yay = args[1] + o.nsy = args[2] + o.result = args[3] + return o + + + def is_accepted(self): + return self.result & 2 > 0 + + + def is_rejected(self): + return self.result & 4 > 0 + + + def is_transferred(self): + return self.result & 24 == 8 + + + def is_voting(self): + return self.result & 7 == 1 + + + def is_executed(self): + return self.result & 8 == 8 + + + def __str__(self): + return "{} {} {} {}".format(self.sender, self.recipient, self.value, self.result) + + + class TransferAuthorization(TxFactory): __abi = None __bytecode = None - + + + def __init__(self, *args, **kwargs): + super(TransferAuthorization, self).__init__(*args, **kwargs) + + + def count(self, *args, **kwargs): + return self.call_noarg('count', *args, **kwargs) + + + def quorum_threshold(self, *args, **kwargs): + return self.call_noarg('quorum', *args, **kwargs) + + + def veto_threshold(self, *args, **kwargs): + return self.call_noarg('vetoThreshold', *args, **kwargs) + + + def signer_count(self, *args, **kwargs): + return self.call_noarg('signerCount', *args, **kwargs) + + + def last_serial(self, *args, **kwargs): + return self.call_noarg('lastSerial', *args, **kwargs) + + + def next_serial(self, *args, **kwargs): + return self.call_noarg('nextSerial', *args, **kwargs) + @staticmethod def abi(): @@ -61,6 +138,26 @@ class TransferAuthorization(TxFactory): return 2800000 + def __single_address_method(self, method, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method(method) + enc.typ(ABIContractType.ADDRESS) + enc.address(address) + data = enc.get() + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format) + return tx + + + def add_writer(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC): + return self.__single_address_method('addWriter', contract_address, sender_address, address, tx_format) + + + def delete_writer(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC): + return self.__single_address_method('deleteWriter', contract_address, sender_address, address, tx_format) + + def create_request(self, contract_address, sender_address, sender, recipient, token, value, tx_format=TxFormat.JSONRPC): enc = ABIContractEncoder() enc.method('createRequest') @@ -79,6 +176,62 @@ class TransferAuthorization(TxFactory): return tx + + def set_thresholds(self, contract_address, sender_address, quorum_threshold, veto_threshold, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('setThresholds') + enc.typ(ABIContractType.UINT32) + enc.typ(ABIContractType.UINT32) + enc.uintn(quorum_threshold, 32) + enc.uintn(veto_threshold, 32) + data = enc.get() + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format) + return tx + + + def requests(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None): + j = JSONRPCRequest(id_generator) + o = j.template() + o['method'] = 'eth_call' + enc = ABIContractEncoder() + enc.method('requests') + enc.typ(ABIContractType.UINT32) + enc.uintn(idx, 32) + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address) + tx = self.set_code(tx, data) + o['params'].append(self.normalize(tx)) + o['params'].append('latest') + o = j.finalize(o) + return o + + + def yay(self, contract_address, sender_address, serial, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('yay') + enc.typ(ABIContractType.UINT32) + enc.uintn(serial, 32) + data = enc.get() + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format) + return tx + + + def nay(self, contract_address, sender_address, serial, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('nay') + enc.typ(ABIContractType.UINT32) + enc.uintn(serial, 32) + data = enc.get() + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format) + return tx + + def constructor(self, sender_address): code = TransferAuthorization.bytecode() tx = self.template(sender_address, None, use_nonce=True) @@ -86,7 +239,7 @@ class TransferAuthorization(TxFactory): return self.build(tx) - def signers(self, contract_address, signer_address, sender_address=ZERO_ADDRESS, id_generator=None): + def writers(self, contract_address, signer_address, sender_address=ZERO_ADDRESS, id_generator=None): j = JSONRPCRequest(id_generator) o = j.template() o['method'] = 'eth_call' @@ -103,8 +256,32 @@ class TransferAuthorization(TxFactory): return o - def have_signer(self, contract_address, signer_address, sender_address=ZERO_ADDRESS): - return self.signers(contract_address, signer_address, sender_address) + def check_result(self, contract_address, sender_address, serial, id_generator=None, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('checkResult') + enc.typ(ABIContractType.UINT32) + enc.uintn(serial, 32) + data = enc.get() + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format) + return tx + + + def execute_request(self, contract_address, sender_address, serial, id_generator=None, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('executeRequest') + enc.typ(ABIContractType.UINT32) + enc.uintn(serial, 32) + data = enc.get() + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format) + return tx + + + def is_writer(self, contract_address, signer_address, sender_address=ZERO_ADDRESS, id_generator=None): + return self.writers(contract_address, signer_address, sender_address) @classmethod @@ -113,6 +290,11 @@ class TransferAuthorization(TxFactory): @classmethod + def parse_count(self, v): + return abi_decode_single(ABIContractType.UINT32, v) + + + @classmethod def parse_create_request_request(self, v): v = strip_0x(v) cursor = 0 @@ -145,21 +327,35 @@ class TransferAuthorization(TxFactory): r = dec.decode() return r -# -# def last_serial(self): -# return self.contract.functions.lastSerial().call() -# -# -# def next_serial(self): -# return self.contract.functions.nextSerial().call() -# -# -# def requests(self, idx): -# req = self.contract.functions.requests(idx).call() -# return { -# 'serial': req[0], -# 'sender': req[1], -# 'recipient': req[2], -# 'token': req[3], -# 'value': req[4], -# } + + @classmethod + def parse_request(self, v): + cursor = 0 + v = strip_0x(v) + d = ABIContractDecoder() + d.typ(ABIContractType.UINT256) + d.typ(ABIContractType.ADDRESS) + d.typ(ABIContractType.ADDRESS) + d.typ(ABIContractType.ADDRESS) + d.typ(ABIContractType.UINT32) + d.typ(ABIContractType.UINT32) + d.typ(ABIContractType.UINT32) + d.typ(ABIContractType.UINT32) + d.val(v[cursor:cursor+64]) + cursor += 64 + d.val(v[cursor:cursor+64]) + cursor += 64 + d.val(v[cursor:cursor+64]) + cursor += 64 + d.val(v[cursor:cursor+64]) + cursor += 64 + d.val(v[cursor:cursor+64]) + cursor += 64 + d.val(v[cursor:cursor+64]) + cursor += 64 + d.val(v[cursor:cursor+64]) + cursor += 64 + d.val(v[cursor:cursor+64]) + cursor += 64 + r = d.decode() + return Request.create(r[1], r[2], r[0], r[3], r[4], r[5], r[6], r[7]) diff --git a/python/erc20_transfer_authorization/unittest/__init__.py b/python/erc20_transfer_authorization/unittest/__init__.py @@ -0,0 +1 @@ +from .base import TestBase diff --git a/python/erc20_transfer_authorization/unittest/base.py b/python/erc20_transfer_authorization/unittest/base.py @@ -0,0 +1,45 @@ +# standard imports +import os +import unittest +import json +import logging + +# external imports +from chainlib.eth.unittest.ethtester import EthTesterCase +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.tx import receipt +from giftable_erc20_token import GiftableToken + +# local imports +from erc20_transfer_authorization import TransferAuthorization + +logg = logging.getLogger() + +testdir = os.path.dirname(__file__) + + +class TestBase(EthTesterCase): + + def setUp(self): + super(TestBase, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.constructor(self.accounts[0]) + + self.rpc.do(o) + + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + self.address = r['contract_address'] + + c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.constructor(self.accounts[0], 'FooToken', 'FOO', 6) + self.rpc.do(o) + + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + self.token_address = r['contract_address'] diff --git a/python/requirements.txt b/python/requirements.txt @@ -1,3 +1,3 @@ confini>=0.5.2,<0.7.0 -chainlib-eth>=0.3.0,<0.4.0 -potaahto~=0.1.1 -\ No newline at end of file +chainlib-eth~=0.4.7 +potaahto~=0.1.1 diff --git a/python/setup.cfg b/python/setup.cfg @@ -26,6 +26,7 @@ python_requires = >= 3.6 packages = erc20_transfer_authorization erc20_transfer_authorization.runnable + erc20_transfer_authorization.unittest [options.package_data] * = diff --git a/python/test_requirements.txt b/python/test_requirements.txt @@ -1,3 +1,3 @@ eth-tester==0.5.0b3 py-evm==0.3.0a20 -eth-erc20~=0.4.0 +eth-erc20~=0.5.2 diff --git a/python/tests/base_erc20transferauthorization.py b/python/tests/base_erc20transferauthorization.py @@ -1,49 +0,0 @@ -# standard imports -import os -import unittest -import json -import logging - -# external imports -from chainlib.eth.unittest.ethtester import EthTesterCase -from chainlib.eth.nonce import RPCNonceOracle -from chainlib.eth.tx import receipt -from giftable_erc20_token import GiftableToken - -# local imports -from erc20_transfer_authorization import TransferAuthorization - -logging.basicConfig(level=logging.DEBUG) -logg = logging.getLogger() - -logging.getLogger('web3').setLevel(logging.WARNING) -logging.getLogger('eth.vm').setLevel(logging.WARNING) - -testdir = os.path.dirname(__file__) - - -class TestBase(EthTesterCase): - - def setUp(self): - super(TestBase, self).setUp() - nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) - c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) - (tx_hash_hex, o) = c.constructor(self.accounts[0]) - - self.rpc.do(o) - - o = receipt(tx_hash_hex) - r = self.rpc.do(o) - self.assertEqual(r['status'], 1) - - self.address = r['contract_address'] - - c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) - (tx_hash_hex, o) = c.constructor(self.accounts[0], 'FooToken', 'FOO', 6) - self.rpc.do(o) - - o = receipt(tx_hash_hex) - r = self.rpc.do(o) - self.assertEqual(r['status'], 1) - - self.token_address = r['contract_address'] diff --git a/python/tests/test_app.py b/python/tests/test_app.py @@ -1,165 +0,0 @@ -# standard imports -import os -import unittest -import logging - -# external imports -from chainlib.eth.nonce import RPCNonceOracle - -# local imports -from erc20_transfer_authorization import TransferAuthorization - -# testutil imports -from tests.base_erc20transferauthorization import TestBase - -logg = logging.getLogger() - -testdir = os.path.dirname(__file__) - - -class ERC20TransferAuthorizationBasicTest(TestBase): - - def test_basic(self): - - nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) - c = TransferAuthorization(self.chain_spec) - - o = c.signers(self.address, self.accounts[0], sender_address=self.accounts[0]) - r = self.rpc.do(o) - self.assertTrue(c.parse_signers(r)) - - o = c.signers(self.address, self.accounts[1], sender_address=self.accounts[0]) - r = self.rpc.do(o) - self.assertFalse(c.parse_signers(r)) - - -# def test_get(self): -# w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) -# t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) -# -# tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) -# r = self.w3.eth.getTransactionReceipt(tx_hash) -# -# topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' -# log = r.logs[0] -# topic = log.topics[0] -# self.assertEqual(topic.hex()[2:], topic_match) -# serial = int(log.data[66:], 16) -# -# for i in range(1, 3): -# tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3+i], t.address, 10*(i+1)).transact({'from': self.w3.eth.accounts[2]}) -# -# tq_count = w.functions.count().call() -# assert (tq_count == 3); -# -# for i in range(w.functions.nextSerial().call(), w.functions.lastSerial().call()): -# tqg = w.functions.requests(i).call() -# -# self.assertEqual(tqg[0], i) -# self.assertEqual(tqg[1], self.w3.eth.accounts[2]) -# self.assertEqual(tqg[2], self.w3.eth.accounts[3+(i-1)]) -# self.assertEqual(tqg[3], t.address) -# self.assertEqual(tqg[4], 10*i) -# -# -# def test_get_vote(self): -# w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) -# t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) -# -# tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) -# w.functions.nay(1).transact({'from': self.w3.eth.accounts[0]}) -# -# self.assertEqual(w.functions.voters(1, 0).call(), self.w3.eth.accounts[0]) -# self.assertEqual(w.functions.vote(1, self.w3.eth.accounts[0]).call(), -1) -# -# -# def test_indexes(self): -# w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) -# t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) -# -# for i in range(0, 5): -# amount = 10*(i+1) -# sender = self.w3.eth.accounts[3+i]; -# tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], sender, t.address, amount).transact({'from': self.w3.eth.accounts[2]}) -# -# w.functions.nay(1).transact({'from': self.w3.eth.accounts[0]}) -# w.functions.nay(4).transact({'from': self.w3.eth.accounts[0]}) -# -# self.assertEqual(w.functions.getSerialAt(0).call(), 2) -# self.assertEqual(w.functions.getSerialAt(1).call(), 3) -# self.assertEqual(w.functions.getSerialAt(2).call(), 5) -# -# -# def test_index_after_removal(self): -# w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) -# t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) -# -# for i in range(0, 4): -# amount = 10*(i+1) -# sender = self.w3.eth.accounts[3+i]; -# tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], sender, t.address, amount).transact({'from': self.w3.eth.accounts[2]}) -# -# tq_count = w.functions.count().call() -# self.assertEqual(tq_count, 4); -# self.assertEqual(w.functions.lastSerial().call(), 4) -# self.assertEqual(w.functions.nextSerial().call(), 1) -# -# w.functions.nay(2).transact({'from': self.w3.eth.accounts[0]}) -# self.assertEqual(w.functions.count().call(), 3) -# self.assertEqual(w.functions.nextSerial().call(), 1) -# self.assertEqual(w.functions.lastSerial().call(), 4) -# -# w.functions.nay(1).transact({'from': self.w3.eth.accounts[0]}) -# self.assertEqual(w.functions.count().call(), 2) -# self.assertEqual(w.functions.nextSerial().call(), 3) -# self.assertEqual(w.functions.lastSerial().call(), 4) -# -# w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 42).transact({'from': self.w3.eth.accounts[2]}) -# self.assertEqual(w.functions.count().call(), 3) -# self.assertEqual(w.functions.lastSerial().call(), 5) -# self.assertEqual(w.functions.nextSerial().call(), 3) -# -# w.functions.nay(3).transact({'from': self.w3.eth.accounts[0]}) -# self.assertEqual(w.functions.count().call(), 2) -# self.assertEqual(w.functions.lastSerial().call(), 5) -# self.assertEqual(w.functions.nextSerial().call(), 4) -# -# w.functions.nay(4).transact({'from': self.w3.eth.accounts[0]}) -# self.assertEqual(w.functions.count().call(), 1) -# self.assertEqual(w.functions.nextSerial().call(), 5) -# self.assertEqual(w.functions.lastSerial().call(), 5) -# -# w.functions.nay(5).transact({'from': self.w3.eth.accounts[0]}) -# self.assertEqual(w.functions.count().call(), 0) -# self.assertEqual(w.functions.nextSerial().call(), 0) -# self.assertEqual(w.functions.lastSerial().call(), 5) -# -# -# def test_pass_on_insufficient_approve(self): -# w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) -# t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) -# -# self.eth_tester.mine_block() -# -# w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[2]}) -# w.functions.yay(1).transact({'from': self.w3.eth.accounts[0]}) -# self.eth_tester.mine_block() -# -# tx_hash = w.functions.executeRequest(1).transact({'from': self.w3.eth.accounts[0]}) -# -# r = self.w3.eth.getTransactionReceipt(tx_hash) -# -# topic_match = 'dab20a0fcd702cf875c2d715d5c3fc99af66a716c94b3405408c94b7311c99eb' # TransferFail(uint256) -# log = r.logs[0] -# topic = log.topics[0] -# self.assertEqual(topic.hex()[2:], topic_match) -# serial = int(log.data[2:], 16) -# -# self.assertEqual(serial, 1) -# -# self.assertEqual(w.functions.count().call(), 0) -# self.assertEqual(w.functions.nextSerial().call(), 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/python/tests/test_basic.py b/python/tests/test_basic.py @@ -0,0 +1,121 @@ +# standard imports +import os +import unittest +import logging + +# external imports +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.tx import receipt + +# local imports +from erc20_transfer_authorization import TransferAuthorization +from erc20_transfer_authorization.unittest import TestBase + +logg = logging.getLogger() + +testdir = os.path.dirname(__file__) + + +class TestBasic(TestBase): + + def setUp(self): + super(TestBasic, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + self.c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = self.c.add_writer(self.address, self.accounts[0], self.accounts[1]) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.add_writer(self.address, self.accounts[0], self.accounts[2]) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + + def test_basic(self): + c = TransferAuthorization(self.chain_spec) + + o = c.is_writer(self.address, self.accounts[0], sender_address=self.accounts[0]) + r = self.rpc.do(o) + self.assertTrue(c.parse_signers(r)) + + o = c.is_writer(self.address, self.accounts[1], sender_address=self.accounts[0]) + r = self.rpc.do(o) + self.assertTrue(c.parse_signers(r)) + + o = c.is_writer(self.address, self.accounts[2], sender_address=self.accounts[0]) + r = self.rpc.do(o) + self.assertTrue(c.parse_signers(r)) + + o = c.is_writer(self.address, self.accounts[3], sender_address=self.accounts[0]) + r = self.rpc.do(o) + self.assertFalse(c.parse_signers(r)) + + o = c.signer_count(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = c.parse_count(r) + self.assertEqual(count, 3) + + +# def test_limit(self): +# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) +# c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) +# +# (tx_hash_hex, o) = c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) +# self.rpc.do(o) +# o = receipt(tx_hash_hex) +# r = self.rpc.do(o) +# self.assertEqual(r['status'], 1) +# +# (tx_hash_hex, o) = c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) +# self.rpc.do(o) +# o = receipt(tx_hash_hex) +# r = self.rpc.do(o) +# self.assertEqual(r['status'], 1) +# +# (tx_hash_hex, o) = c.yay(self.address, self.accounts[0], 1) +# self.rpc.do(o) +# o = receipt(tx_hash_hex) +# r = self.rpc.do(o) +# self.assertEqual(r['status'], 1) +# +# (tx_hash_hex, o) = c.yay(self.address, self.accounts[0], 2) +# self.rpc.do(o) +# o = receipt(tx_hash_hex) +# r = self.rpc.do(o) +# self.assertEqual(r['status'], 1) +# +# (tx_hash_hex, o) = c.check_result(self.address, self.accounts[0], 1) +# self.rpc.do(o) +# o = receipt(tx_hash_hex) +# r = self.rpc.do(o) +# self.assertEqual(r['status'], 1) +# +# o = c.requests(self.address, 1, sender_address=self.accounts[0]) +# r = self.rpc.do(o) +# request = self.c.parse_request(r) +# logg.debug('request {}'.format(request)) +# self.assertTrue(request.is_accepted()) + + + def test_serial(self): + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + for i in range(5): + (tx_hash_hex, o) = c.create_request(self.address, self.accounts[0], self.accounts[i], self.accounts[i+1], self.token_address, 1024 * i) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = c.count(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = c.parse_count(r) + self.assertEqual(count, 5) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/tests/test_quorum.py b/python/tests/test_quorum.py @@ -1,298 +1,516 @@ # standard imports -import logging +import os import unittest +import logging -# testutil imports -from tests.base_erc20transferauthorization import TestBase - -logg = logging.getLogger() - -rejected_log_signature = '3d61d434b895790b08f040c45261fce3b3bec596278b3a0f25dd9f741d0ba469' -vetoed_log_signature = '1ad80b2541a1f52bdc838332d7c23606116a1188a8cbbc4c0948b4b56ce51d14' -approved_log_signature = '36ea04725f8aa40ee603224671681b753f9cba3cb5f67c5a0e24a3b39900c065' - - -class ERC20TransferAuthorizationQuorumTest(TestBase): - - @unittest.skip('must be ported to chainlib') - def test_vote_access(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - - w.functions.addSigner(self.w3.eth.accounts[5]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.setThresholds(2, 0).transact({'from': self.w3.eth.accounts[0]}) - - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) - - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) - - # only signers may vote - with self.assertRaises(Exception): - tx_hashh = w.functions.yay(serial).transact({'from': self.w3.eth.accounts[2]}) - - # only signers may vote - with self.assertRaises(Exception): - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[2]}) - - w.functions.yay(serial).transact({'from': self.w3.eth.accounts[0]}) - - # may not vote twice - with self.assertRaises(Exception): - tx_hashh = w.functions.yay(serial).transact({'from': self.w3.eth.accounts[0]}) - - # may not change vote - with self.assertRaises(Exception): - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[0]}) - - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[5]}) - - # may not vote twice - with self.assertRaises(Exception): - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[5]}) - - # may not change vote - with self.assertRaises(Exception): - tx_hashh = w.functions.yay(serial).transact({'from': self.w3.eth.accounts[5]}) - - - @unittest.skip('must be ported to chainlib') - def test_minimal_quorum(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - - t.functions.approve(w.address, 10).transact({'from': self.w3.eth.accounts[2]}) - self.eth_tester.mine_block() - - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) - - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) - - with self.assertRaises(Exception): - w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) - - # only signers may vote - with self.assertRaises(Exception): - tx_hashh = w.functions.yay(serial).transact({'from': self.w3.eth.accounts[2]}) - - tx_hashh = w.functions.yay(serial).transact({'from': self.w3.eth.accounts[0]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - - topic_match = approved_log_signature - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - - tx_hashh = w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - - topic_match = 'bcf6a68a2f901be4a23a41b53acd7697893a7e34def4e28acba584da75283b67' # Executed(serial) - log = r.logs[1] - topic = log.topics[0].hex()[2:] - self.assertEqual(topic, topic_match) - - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[2]).call(), 90) - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[3]).call(), 10) - - - @unittest.skip('must be ported to chainlib') - def test_simple_quorum(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - - t.functions.approve(w.address, 10).transact({'from': self.w3.eth.accounts[2]}) - self.eth_tester.mine_block() - - w.functions.addSigner(self.w3.eth.accounts[5]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.addSigner(self.w3.eth.accounts[6]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.setThresholds(2, 0).transact({'from': self.w3.eth.accounts[0]}) - - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) - - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) - - tx_hashh = w.functions.yay(serial).transact({'from': self.w3.eth.accounts[0]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - self.assertEqual(len(r.logs), 0) - - # attempt to execute fails - with self.assertRaises(Exception): - w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) - - # dough is still there - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[2]).call(), 100) +# external imports +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.address import is_same_address +from chainlib.eth.tx import receipt - w.functions.yay(serial).transact({'from': self.w3.eth.accounts[5]}) - - w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) +# local imports +from erc20_transfer_authorization import TransferAuthorization +from erc20_transfer_authorization.unittest import TestBase - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[2]).call(), 90) - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[3]).call(), 10) - - # additional votes not possible - with self.assertRaises(Exception): - w.functions.yay(serial).transact({'from': self.w3.eth.accounts[6]}) +logg = logging.getLogger() - with self.assertRaises(Exception): - w.functions.nay(serial).transact({'from': self.w3.eth.accounts[6]}) +testdir = os.path.dirname(__file__) + + +class TestQuorum(TestBase): + + def setUp(self): + super(TestQuorum, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + self.c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + + for i in range(1, 5): + (tx_hash_hex, o) = self.c.add_writer(self.address, self.accounts[0], self.accounts[i]) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + + def test_basic(self): + o = self.c.quorum_threshold(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 1) + + o = self.c.veto_threshold(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 0) + + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 3, 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.quorum_threshold(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 3) + + o = self.c.veto_threshold(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 1) + + + def test_signer_sanity(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 0, 0) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 0, 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 5, 0) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 6, 0) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 5, 5) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 5, 6) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + + def test_register(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(is_same_address(request.sender, self.accounts[1])) + self.assertTrue(is_same_address(request.recipient, self.accounts[2])) + self.assertTrue(is_same_address(request.token, self.token_address)) + self.assertEqual(request.value, 1024) + self.assertEqual(request.serial, 1) + self.assertEqual(request.yay, 1) + self.assertEqual(request.nay, 0) + + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) - @unittest.skip('must be ported to chainlib') - def test_minimal_rejection(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) - - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) - - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[0]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - - topic_match = rejected_log_signature - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - - # cannot execute - with self.assertRaises(Exception): - w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) - - - @unittest.skip('must be ported to chainlib') - def test_simple_rejection(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - - t.functions.approve(w.address, 10).transact({'from': self.w3.eth.accounts[2]}) - self.eth_tester.mine_block() - - w.functions.addSigner(self.w3.eth.accounts[5]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.addSigner(self.w3.eth.accounts[6]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.setThresholds(2, 0).transact({'from': self.w3.eth.accounts[0]}) - - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) - - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) - - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[0]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - self.assertEqual(len(r.logs), 0) - - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[5]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - - topic_match = rejected_log_signature - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - - # cannot execute - with self.assertRaises(Exception): - w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) - - # additional votes not possible - with self.assertRaises(Exception): - w.functions.yay(serial).transact({'from': self.w3.eth.accounts[6]}) - - with self.assertRaises(Exception): - w.functions.nay(serial).transact({'from': self.w3.eth.accounts[6]}) - - - @unittest.skip('must be ported to chainlib') def test_veto(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - - t.functions.approve(w.address, 10).transact({'from': self.w3.eth.accounts[2]}) - self.eth_tester.mine_block() - - w.functions.addSigner(self.w3.eth.accounts[5]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.addSigner(self.w3.eth.accounts[6]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.setThresholds(2, 1).transact({'from': self.w3.eth.accounts[0]}) - - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) - - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) - - w.functions.yay(serial).transact({'from': self.w3.eth.accounts[0]}) - tx_hashh = w.functions.nay(serial).transact({'from': self.w3.eth.accounts[5]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - - topic_match = vetoed_log_signature - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - - # cannot execute - with self.assertRaises(Exception): - w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) - - # additional votes not possible - with self.assertRaises(Exception): - w.functions.yay(serial).transact({'from': self.w3.eth.accounts[6]}) - - with self.assertRaises(Exception): - w.functions.nay(serial).transact({'from': self.w3.eth.accounts[6]}) - - - @unittest.skip('must be ported to chainlib') - def test_inflight_change(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) - - w.functions.addSigner(self.w3.eth.accounts[5]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.addSigner(self.w3.eth.accounts[6]).transact({'from': self.w3.eth.accounts[0]}) - w.functions.setThresholds(2, 0).transact({'from': self.w3.eth.accounts[0]}) - - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) - - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) - - w.functions.nay(serial).transact({'from': self.w3.eth.accounts[0]}) - w.functions.removeSigner(self.w3.eth.accounts[6]).transact({'from': self.w3.eth.accounts[0]}) - - tx_hashh = w.functions.checkResult(serial).transact({'from': self.w3.eth.accounts[6]}) - r = self.w3.eth.getTransactionReceipt(tx_hashh) - - topic_match = rejected_log_signature - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 1, 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_rejected()) + + + def test_simple_yay(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_accepted()) + + + def test_nay_before_yay(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertFalse(request.is_accepted()) + + + def test_yay_nay_yay(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_accepted()) + + + def test_yay_yay_nay(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + logg.debug('request {}'.format(request)) + self.assertTrue(request.is_accepted()) + + + def test_nay_yay_nay(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_rejected()) + + + def test_nay_yay_nay_yay(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + logg.debug('request {}'.format(request)) + self.assertTrue(request.is_rejected()) + + + def test_nay_yay_nay_nay(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 2, 2) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.yay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + logg.debug('request {}'.format(request)) + self.assertTrue(request.is_rejected()) + + + def test_nay_majority(self): + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 3, 0) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[1], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[1], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) + c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.nay(self.address, self.accounts[2], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + logg.debug('request {}'.format(request)) + self.assertTrue(request.is_rejected()) if __name__ == '__main__': diff --git a/python/tests/test_request.py b/python/tests/test_request.py @@ -0,0 +1,275 @@ +# standard imports +import os +import unittest +import logging +import random + +# external imports +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.address import is_same_address +from giftable_erc20_token import GiftableToken +from eth_erc20 import ERC20 +from chainlib.eth.tx import receipt + +# local imports +from erc20_transfer_authorization import TransferAuthorization +from erc20_transfer_authorization.unittest import TestBase + +#logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +testdir = os.path.dirname(__file__) + + +class TestRequest(TestBase): + + def setUp(self): + super(TestRequest, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + self.c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + + c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.mint_to(self.token_address, self.accounts[0], self.accounts[9], 10000) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[9], self.rpc) + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.approve(self.token_address, self.accounts[9], self.address, 10000) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + + def test_basic(self): + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 0) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 0) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[2], self.token_address, 1) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[3], self.token_address, 2) + self.rpc.do(o) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 2) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 1) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 1) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.execute_request(self.address, self.accounts[0], 1) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.count(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 1) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 2) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 2) + + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], 2) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 2) + self.rpc.do(o) + + o = self.c.count(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 0) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 2) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 0) + + + def test_hi_lo(self): + for i in range(5): + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[i+1], self.token_address, i+1) + self.rpc.do(o) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 5) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 6) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 5) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 5) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.execute_request(self.address, self.accounts[0], 5) + self.rpc.do(o) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 5) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 1) + + o = self.c.requests(self.address, 5, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_executed()) + + o = self.c.count(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 4) + + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[0], self.token_address, 100) + self.rpc.do(o) + + o = self.c.count(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 5) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 6) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 1) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 6) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 6) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.execute_request(self.address, self.accounts[0], 6) + self.rpc.do(o) + + o = self.c.requests(self.address, 6, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_executed()) + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 6) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 1) + + c = ERC20(self.chain_spec) + o = c.balance(self.token_address, self.accounts[0], sender_address=self.accounts[0]) + r = self.rpc.do(o) + balance = c.parse_balance(r) + self.assertEqual(balance, 100) + + o = c.balance(self.token_address, self.accounts[5], sender_address=self.accounts[0]) + r = self.rpc.do(o) + balance = c.parse_balance(r) + self.assertEqual(balance, 5) + + o = c.balance(self.token_address, self.accounts[9], sender_address=self.accounts[0]) + r = self.rpc.do(o) + balance = c.parse_balance(r) + self.assertEqual(balance, 10000-100-5) + + + def test_hi_lo_shuffle(self): + for i in range(100): + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[(i+1)%8], self.token_address, i+1) + self.rpc.do(o) + + v = list(range(1, 101)) + c = 0 + random.shuffle(v) + for i in v: + logg.info('processing request number {} at index {}'.format(c, i)) + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], i) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], i) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + o = self.c.requests(self.address, i, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_rejected()) + + c += 1 + o = self.c.count(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + count = self.c.parse_count(r) + self.assertEqual(count, 100-c) + + + o = self.c.last_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 100) + + o = self.c.next_serial(self.address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + serial = self.c.parse_count(r) + self.assertEqual(serial, 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/tests/test_transfer.py b/python/tests/test_transfer.py @@ -1,46 +1,138 @@ # standard imports -import logging +import os import unittest +import logging + +# external imports +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.address import is_same_address +from chainlib.eth.tx import receipt +from giftable_erc20_token import GiftableToken +from eth_erc20 import ERC20 -# testutil imports -from tests.base_erc20transferauthorization import TestBase +# local imports +from erc20_transfer_authorization import TransferAuthorization +from erc20_transfer_authorization.unittest import TestBase logg = logging.getLogger() +testdir = os.path.dirname(__file__) -class ERC20TransferAuthorizationTransferTest(TestBase): - @unittest.skip('must be ported to chainlib') - def test_transfer(self): - w = self.w3.eth.contract(abi=self.abi_wallet, address=self.address_wallet) - t = self.w3.eth.contract(abi=self.abi_token, address=self.address_token) +class TestTransfer(TestBase): + + def setUp(self): + super(TestTransfer, self).setUp() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + self.c = TransferAuthorization(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + + c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.mint_to(self.token_address, self.accounts[0], self.accounts[9], 10000) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + nonce_oracle = RPCNonceOracle(self.accounts[9], self.rpc) + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.approve(self.token_address, self.accounts[9], self.address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + + def test_premature_transfer(self): + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) - t.functions.approve(w.address, 10).transact({'from': self.w3.eth.accounts[2]}) - self.eth_tester.mine_block() + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 1) + r = self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) - tx_hash = w.functions.createRequest(self.w3.eth.accounts[2], self.w3.eth.accounts[3], t.address, 10).transact({'from': self.w3.eth.accounts[9]}) - r = self.w3.eth.getTransactionReceipt(tx_hash) + (tx_hash_hex, o) = self.c.execute_request(self.address, self.accounts[0], 1) + r = self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) - topic_match = 'b609ae609609ee99268d05bc1371102cafe8d6b964bf082439ab16be2a01c87c' - log = r.logs[0] - topic = log.topics[0] - self.assertEqual(topic.hex()[2:], topic_match) - serial = int(log.data[66:], 16) + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_voting()) + self.assertFalse(request.is_executed()) + + + def test_reject_transfer(self): + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash_hex, o) = self.c.set_thresholds(self.address, self.accounts[0], 1, 1) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.nay(self.address, self.accounts[0], 1) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 1) + r = self.rpc.do(o) + + (tx_hash_hex, o) = self.c.execute_request(self.address, self.accounts[0], 1) + r = self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) + + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + self.assertTrue(request.is_rejected()) + self.assertFalse(request.is_voting()) + self.assertFalse(request.is_executed()) + + + def test_transfer(self): + (tx_hash_hex, o) = self.c.create_request(self.address, self.accounts[0], self.accounts[9], self.accounts[2], self.token_address, 1024) + self.rpc.do(o) + + (tx_hash_hex, o) = self.c.yay(self.address, self.accounts[0], 1) + self.rpc.do(o) - w.functions.yay(serial).transact({'from': self.w3.eth.accounts[0]}) - w.functions.executeRequest(serial).transact({'from': self.w3.eth.accounts[0]}) + (tx_hash_hex, o) = self.c.check_result(self.address, self.accounts[0], 1) + r = self.rpc.do(o) - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[2]).call(), 90) - self.assertEqual(t.functions.balanceOf(self.w3.eth.accounts[3]).call(), 10) + (tx_hash_hex, o) = self.c.execute_request(self.address, self.accounts[0], 1) + r = self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) - req = w.functions.requests(1).call() - self.assertEqual(req[7], self.w3.eth.blockNumber) + o = self.c.requests(self.address, 1, sender_address=self.accounts[0]) + r = self.rpc.do(o) + request = self.c.parse_request(r) + logg.debug('request {}'.format(request)) + self.assertTrue(request.is_transferred()) + self.assertTrue(request.is_executed()) - serial_compare = w.functions.requestSenderIndex(self.w3.eth.accounts[2], 0).call() - self.assertEqual(serial_compare, req[0]) + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + c = ERC20(self.chain_spec, nonce_oracle=nonce_oracle) + o = c.balance(self.token_address, self.accounts[9], sender_address=self.accounts[0]) + r = self.rpc.do(o) + balance = c.parse_balance(r) + self.assertEqual(balance, 10000-1024) - serial_compare = w.functions.requestRecipientIndex(self.w3.eth.accounts[3], 0).call() - self.assertEqual(serial_compare, req[0]) + (tx_hash_hex, o) = self.c.execute_request(self.address, self.accounts[0], 1) + r = self.rpc.do(o) + o = receipt(tx_hash_hex) + r = self.rpc.do(o) + self.assertEqual(r['status'], 0) if __name__ == '__main__': diff --git a/solidity/ERC20TransferAuthorization.sol b/solidity/ERC20TransferAuthorization.sol @@ -1,75 +1,86 @@ -pragma solidity >0.6.11; +pragma solidity >0.8.0; // SPDX-License-Identifier: GPL-3.0-or-later contract ERC20TransferAuthorization { struct Transaction { - uint256 serial; + uint256 value; address sender; address recipient; address token; - uint256 value; - uint256 yay; - uint256 nay; - uint256 blockNumber; - int8 result; // -1 rejected/vetoed, 0 = completed, 1 = voting, 2 = approved + uint32 serial; + uint32 yay; + uint32 nay; + //uint256 blockNumber; + // bit 1: started + // bit 2: approved + // bit 3: rejected + // bit 4: finalized + // bit 5: transfererror + uint8 result; } - mapping ( uint256 => mapping ( address => int8 )) public vote; - mapping ( uint256 => address[] ) public voters; + mapping ( uint32 => mapping ( address => int8 )) public vote; + //mapping ( uint256 => address[] ) public voters; + mapping( uint32 => Transaction ) public requests; + //mapping(address => uint256[]) public requestSenderIndex; + //mapping(address => uint256[]) public requestRecipientIndex; address public owner; - mapping(uint256 => Transaction) public requests; - mapping(address => uint256[]) public requestSenderIndex; - mapping(address => uint256[]) public requestRecipientIndex; - uint256 hi; - uint256 lo; - int256 public count; - uint256 public quorum; - uint256 public vetoThreshold; - uint256 public signerCount; + uint32 hi; + uint32 lo; + uint32 public count; + uint32 public quorum; + uint32 public vetoThreshold; + uint32 public signerCount; mapping(address => bool) public signers; - - event NewRequest(address indexed _sender, address indexed _recipient, address indexed _token, uint256 _value, uint256 _serial); - event Executed(uint256 _serial); - event TransferFail(uint256 _serial); - event QuorumSet(uint256 _quorum, uint256 _vetoThreshold, uint256 _signerCount); - event SignerAdded(address _signer); - event SignerRemoved(address _signer); - event Vetoed(uint256 indexed _serial, uint256 _yays, uint256 _nays); - event Approved(uint256 indexed _serial, uint256 _yays, uint256 _nays); - event Rejected(uint256 indexed _serial, uint256 _yays, uint256 _nays); + address[] public writers; + + event NewRequest(address indexed _sender, address indexed _recipient, address indexed _token, uint256 _value, uint32 _serial); + event Executed(uint32 _serial); + event TransferFail(uint32 _serial); + event QuorumSet(uint32 indexed _quorum, uint32 indexed _vetoThreshold, uint32 indexed _signerCount); + event WriterAdded(address _signer); + event WriterRemoved(address _signer); + event Vetoed(uint32 indexed _serial, uint32 indexed _yays, uint32 indexed _nays); + event Approved(uint32 indexed _serial, uint32 indexed _yays, uint32 indexed _nays); + event Rejected(uint32 indexed _serial, uint32 indexed _yays, uint32 indexed _nays); constructor() public { owner = msg.sender; hi = 1; lo = 1; - addSigner(msg.sender); + addWriter(msg.sender); setThresholds(1, 0); } - function addSigner(address _signer) public returns (uint256) { + function isWriter(address _signer) public view returns (bool) { + return signers[_signer]; + } + + function addWriter(address _signer) public returns (uint32) { require(msg.sender == owner, 'ERR_ACCESS'); require(signers[_signer] == false, 'ERR_NOTFOUND'); signers[_signer] = true; signerCount++; - emit SignerAdded(_signer); + emit WriterAdded(_signer); return signerCount; } - function removeSigner(address _signer) public returns (uint256) { - require(msg.sender == owner || msg.sender == _signer, 'ERR_ACCESS'); + function removeWriter(address _signer) public returns (uint32) { + //require(msg.sender == owner || msg.sender == _signer, 'ERR_ACCESS'); + require(msg.sender == owner, 'ERR_ACCESS'); require(signers[_signer] == true, 'ERR_NOTFOUND'); require(signerCount > quorum && signerCount > vetoThreshold, 'ERR_REDUCE_THRESHOLD_FIRST'); signers[_signer] = false; signerCount--; - emit SignerRemoved(_signer); + emit WriterRemoved(_signer); return signerCount; } - function setThresholds(uint256 _quorum, uint256 _vetoThreshold) public returns (bool) { + function setThresholds(uint32 _quorum, uint32 _vetoThreshold) public returns (bool) { require(_quorum <= signerCount); require(_quorum > 0); require(_vetoThreshold <= signerCount); @@ -81,7 +92,7 @@ contract ERC20TransferAuthorization { } // create new request - function createRequest(address _sender, address _recipient, address _token, uint256 _value) public returns (uint256) { + function createRequest(address _sender, address _recipient, address _token, uint256 _value) public returns (uint32) { Transaction storage txx = requests[hi]; txx.serial = hi; @@ -91,7 +102,7 @@ contract ERC20TransferAuthorization { txx.value = _value; txx.result = 1; - count++; + count++; hi++; emit NewRequest(txx.sender, txx.recipient, txx.token, txx.value, txx.serial); @@ -101,12 +112,12 @@ contract ERC20TransferAuthorization { // if request was oldest in index, move the pointer to oldest request to next oldest unfinished request. // if no unfinished requests exits, it will point to newest request - function removeItem(uint256 _serialToRemove) private returns (uint256) { + function removeItem(uint32 _serialToRemove) private returns (uint32) { count--; if (count > 0) { if (_serialToRemove == lo) { - uint256 i; + uint32 i; i = getSerialAt(0); if (i == 0) { lo = hi; @@ -114,7 +125,7 @@ contract ERC20TransferAuthorization { lo = i; } } - } else { + } else if (lo != hi) { lo = hi; } @@ -122,24 +133,36 @@ contract ERC20TransferAuthorization { } // index of newest vote - function lastSerial() public view returns ( uint256 ) { + function lastSerial() public view returns ( uint32 ) { return hi - 1; } // index of oldest unfinished vote - function nextSerial() public view returns ( uint256 ) { + function nextSerial() public view returns ( uint32 ) { if (hi - lo == 0) { + if (hi == 1) { + return 0; + } + Transaction storage txx = requests[lo]; + if (txx.result > 0) { + return lo; + } return 0; } return lo; } - // get the unfinished vote at the given index - function getSerialAt(uint256 _idx) public view returns ( uint256 ) { - uint256 i; - for (uint256 j = lo; j < hi; j++) { + // get the nth unfinished vote, where nth is _idx, starting at 0 + function getSerialAt(uint32 _idx) public view returns ( uint32 ) { + uint32 i; + + if (lo == hi) { + return 0; + } + + for (uint32 j = lo; j < hi; j++) { Transaction storage txx = requests[j]; - if (txx.result > 0) { + if (txx.result & 7 == 1) { if (i == _idx) { return txx.serial; } @@ -150,7 +173,7 @@ contract ERC20TransferAuthorization { } // vote yay, one per signer - function yay(uint256 _serial) public returns (uint256) { + function yay(uint32 _serial) public returns (uint32) { require(signers[msg.sender], 'ERR_ACCESS'); require(vote[_serial][msg.sender] == 0, 'ERR_ALREADYVOTED'); @@ -158,7 +181,7 @@ contract ERC20TransferAuthorization { require(txx.result == 1); vote[txx.serial][msg.sender] = 1; - voters[txx.serial].push(msg.sender); + //voters[txx.serial].push(msg.sender); txx.yay++; checkResult(txx.serial); @@ -167,7 +190,7 @@ contract ERC20TransferAuthorization { } // vote nay, one per signer - function nay(uint256 _serial) public returns (uint256) { + function nay(uint32 _serial) public returns (uint32) { require(signers[msg.sender], 'ERR_ACCESS'); require(vote[_serial][msg.sender] == 0, 'ERR_ALREADYVOTED'); @@ -175,7 +198,7 @@ contract ERC20TransferAuthorization { require(txx.result == 1); vote[txx.serial][msg.sender] = -1; - voters[txx.serial].push(msg.sender); + //voters[txx.serial].push(msg.sender); txx.nay++; checkResult(txx.serial); @@ -185,25 +208,25 @@ contract ERC20TransferAuthorization { // locks the state of the vote if quorum or veto is reached // returns true if state changes - function checkResult(uint256 _serial) public returns (bool) { + function checkResult(uint32 _serial) public returns (bool) { bool result; Transaction storage txx = requests[_serial]; - if (txx.result < 1) { + if (txx.result < 1 || txx.result & 6 > 0) { return result; } if (txx.yay >= quorum) { - txx.result = 2; + txx.result |= 2; emit Approved(txx.serial, txx.yay, txx.nay); result = true; } else if (vetoThreshold > 0 && txx.nay >= vetoThreshold) { - txx.result = -1; + txx.result |= 4; removeItem(txx.serial); emit Vetoed(txx.serial, txx.yay, txx.nay); result = true; } else if (signerCount - txx.nay < quorum) { - txx.result = -1; + txx.result |= 4; removeItem(txx.serial); emit Rejected(txx.serial, txx.yay, txx.nay); result = true; @@ -213,24 +236,24 @@ contract ERC20TransferAuthorization { } // execute transfer. needs positive vote result - function executeRequest(uint256 _serial) public returns (bool) { + function executeRequest(uint32 _serial) public returns (bool) { Transaction storage txx = requests[_serial]; require(txx.serial > 0, 'ERR_INVALID_REQUEST'); - require(txx.result == 2, 'ERR_NOT_ENDORSED'); + require(txx.result & 11 == 3, 'ERR_NOT_ENDORSED'); - txx.result = 0; + removeItem(txx.serial); + txx.result |= 8; (bool success, bytes memory _r) = txx.token.call(abi.encodeWithSignature("transferFrom(address,address,uint256)", txx.sender, txx.recipient, txx.value)); - removeItem(txx.serial); - - txx.blockNumber = block.number; - requestSenderIndex[txx.sender].push(txx.serial); - requestRecipientIndex[txx.recipient].push(txx.serial); + //txx.blockNumber = block.number; + //requestSenderIndex[txx.sender].push(txx.serial); + //requestRecipientIndex[txx.recipient].push(txx.serial); if (success) { emit Executed(_serial); } else { + txx.result |= 16; // this edit is for convenience only. since bit 4 is already set, it is not re-entrant. emit TransferFail(_serial); }