This project describes a road system that will be represent by 2 overarching smart contracts:
Regulator
TollBoothOperator
These other elements of the system are represented by externally owned accounts:
- owner of
Regulator
- owner of
TollBoothOperator
- individual vehicles
- individual toll booths
The price will be decided by 3 variables:
- the entry toll booth
- the exit toll booth
- the vehicle type
These variables will be used thus:
- the
TollBoothOperator
defines a base route price from the entry booth to the exit booth. The base route price from the exit booth to the entry booth may be non-existent, equal or different. - the
TollBoothOperator
defines a multiplier for each vehicle type. This is the number by which the base route price is multiplied with to get a specific vehicles route price or deposit.
There are a certain number of off-chain actions, such as proof of identity or secret exchange, that we will address only with a wave of the hand in this project. Smart contracts should make these actions possible via public functions, though.
We only care about vehicles, not about drivers. It means it can be driven by the vehicle's rightful owner, another driver, or no human driver at all.
-
Only vehicles registered with the regulator are allowed to enter the road system.
-
Before entering the road system, registered vehicles must make a deposit of at least the amount required by the operator for their vehicle type.
-
Upon exit, the exit toll booth will trigger the payment off the deposit and refund the difference to the vehicle.
-
This deposit must be accompanied by the address of the toll booth by which they will enter.
-
When exiting the road system, the vehicle gives, off-chain, a secret to the exit toll booth.
-
The exit toll booth sends this secret to the toll booth operator contract, which is used to unlock the deposit, then pay the toll booth operator the proper fee for the route taken, then refunds the difference to the vehicle.
The off-chain exchange of the secret is handled by a wave of the hand in this project. You are not asked to work on this massive "detail".
- When faced with a vehicle that wants to enter the road system, a toll booth will confirm, off-chain, the address of the incoming vehicle.
- If the vehicle has made the proper deposit for its type, the booth opens the gate. This part will be simulated in tests.
Here too, you need not work on the off-chain exchange of address of the incoming vehicles.
This account will:
- update the vehicle types in the smart contract.
- ask the smart contract to deploy new
TollBoothOperator
s. - inform whether an address is a registered
TollBoothOperator
. - unregister
ToolBoothOperator
s when needed.
This account will:
- update the base price of routes for all toll booth pairs it sees fit.
- update the multiplier of all vehicle types it sees fit.
In particular:
- if a vehicle just completed a route for which there is no base price, the owner should put a price to it before it can unlock the deposit.
- if a vehicle type has no multiplier, then the road system is closed to this vehicle type.
We will simplify the system by overlooking the fact that the owner may disallow a vehicle type after such a vehicle has entered the system.
This contract does 2 main things (studiously avoiding the word functions here):
- keep track of the vehicle type for each vehicle address
- deploy new
TollBoothOperator
This ensures that:
- because the regulator deploys the toll booth operator, road users are confident they are exchanging with a vetted smart contract.
- an address that has no vehicle type is not registered and should not be allowed onto the road system.
Additionally:
- the regulator collects no fees.
- a type of
0
denotes an unregistered vehicle. - for mnemonics only, you can assign type
1
for motorbikes,2
for cars and3
for lorries.
This contract has many functions which have been parcelled out to its inheritance tree, see its interfaces. The things it does:
- can be paused / resumed to pause the vehicle-facing operations.
- keeps track of the regulator.
- keeps track of the base deposit.
- keeps track of the multipliers of vehicle types.
- keeps track of the toll booths under its purview.
- keeps track of the route base prices.
- accepts deposits from vehicles.
- accepts exit calls from toll booths.
- accepts messages to clear the exit backlog.
For simplicity's sake:
- there is only 1 (or 0) toll booth at a given kilometre on the road. So one booth may have as many gates as there are lanes on the road.
- a base route price of
0
denotes an absence of information. - the entry and exit booths of a route cannot be the same address.
- the
TollBoothOperator
has total control over the deposit, the base route prices, and the multipliers. - there is no congestion or pollution charges.
- the latest route base price is the valid route base price, even if the toll booth operator changed the price after the vehicle entered the road system.
For a vehicle to be accepted on the road system and the operator to be paid at the end of the route, a little dance is engineered:
- before entering the system, the vehicle deposits the required amount into the
TollBoothOperator
, and passes along the address of the entry booth and a unique hashed secret of its own choice. The vehicle keeps the secret until the end of the trip. - when faced with the toll booth it mentioned when depositing, it proves its identity off-chain (we talk about this identity proof but you need not implement it), after which the booth opens the gate.
- when exiting the road system at a booth, the vehicle gives the exit toll booth its unhashed secret off-chain. Again, we talk about this off-chain exchange but you do not need to implement it.
- the exit toll booth then submits this secret to the
TollBoothOperator
, which unlocks the deposit for payment and refund of the difference, if applicable. - if the fee is equal to or higher than the deposit, then the whole deposit is used and no more is asked of the vehicle, now or before any future trip.
- if the fee is smaller than the deposit, then the difference is returned to the vehicle.
- if the fee is not known at the time of exit, i.e. if the fee is
0
, the pending payment is recorded, and "base route price required" event is emitted and listened to by the operator's oracle. - when the oracle receives a new base route price request, it submits the base fee, which also clears one pending payment.
- if there are more than 1 pending payments, an additional function is there to progressively clear the backlog a set number of pending payments at a time in a FIFO manner. If both vehicleA and vehicleB entered at the same booths and exited at the same booths, then if vehicleA exited before vehicleB, then vehicleA should pop from the FIFO ahead of vehicleB.
- the vehicle type and multiplier to be used are those at the time of the transaction. So an entry transaction uses values at that time, an exit transaction uses values at that time.
For simplicity's sake, we have not implemented a deadline after which the deposit is returned to the vehicle. Also, the function to clear pending more than 1 payment is an ugly one, because it implies a loop and multiple transfers. We actually use it to see how many pending payments you can cram in a single transaction.
All interfaces are found in the contracts/interfaces/
folder and are suffixed with I
, a capital i
. Here is the list of them:
OwnedI
, which keeps track of the owner of a contract.PausableI
, which keeps track of the paused status of a contract.RegulatorI
, which describes methods required of the regulator.RegulatedI
, which keeps track of who the regulator is.MultiplierHolderI
, which keeps track of the multipliers to attach to vehicle types.DepositHolderI
, which keeps track of the base deposit required of vehicles.TollBoothHolderI
, which keeps track of addresses that represent toll booths in the system.RoutePriceHolderI
, which keeps track of the base route prices between entry and exit toll booths.TollBoothOperatorI
, which describes methods required of the toll booth operator for interaction with vehicles.
It extends:
OwnedI
and has:
- a modifier named
fromOwner
that rolls back the transaction if the transaction sender is not the owner. - a constructor that takes no parameter.
It extends:
OwnedI
PausableI
and has:
- a modifier named
whenPaused
that rolls back the transaction if the contract is in thefalse
paused state. - a modifier named
whenNotPaused
that rolls back the transaction if the contract is in thetrue
paused state - a constructor that takes one
bool
parameter, the initial paused state.
It extends:
OwnedI
RegulatorI
and has:
- a constructor that takes no parameter.
It extends:
RegulatedI
and has:
- a constructor that takes one
address
parameter, the initial regulator; it should roll back the transaction if the regulator argument is0
.
It extends:
OwnedI
MultiplierHolderI
and has:
- a constructor that takes no parameter.
It extends:
OwnedI
DepositHolderI
and has:
- a constructor that takes one
uint
parameter, the initial deposit wei value; it should roll back the transaction if the deposit argument is0
.
It extends:
OwnedI
TollBoothHolderI
and has:
- a constructor that takes no parameter.
It extends:
OwnedI
TollBoothHolderI
RoutePriceHolderI
and has:
- a constructor that takes no parameter.
It extends:
OwnedI
PausableI
RegulatedI
MultiplierHolderI
DepositHolderI
TollBoothHolderI
RoutePriceHolderI
TollBoothOperatorI
and has:
- a constructor that takes in this order:
- one
bool
parameter, the initial paused state. - one
uint
parameter, the initial deposit wei value; it should roll back the transaction if the deposit argument is0
. - one
address
parameter, the initial regulator; it should roll back the transaction if the regulator argument is0
.
- one
- a fallback function that rejects all incoming calls.
You need to create one migration script 2_...js
that will:
- deploy a regulator,
- then call
createNewOperator
on it. - then resumes the newly created operator, which should be paused before the resume step.
$ npm -v 5.6.0
webpack -v 3.11.0
node -v v8.11.3
php -v PHP 7.0.28-0ubuntu0.16.04.1 (cli) ( NTS )
Ganache CLI v6.1.6
Truffle v3.4.11
available ports are open
this is based on using localhost
if you are using named/ip address then truffle.js
needs to be updated and you need to specify -h
flag with ganache-cli
- clone to linux machine
- cd into dir
- start ganache
ganache-cli
- migrate contracts
truffle migrate --reset
- this will provide you with regulator address. make note of this as it is needed
- install react packes with npm
npm i
- build web app
./node_modules/.bin/webpack
- run web service
php -S 0.0.0.0:8000 -t /home/ubuntu/shorif2000-code/build/
- cd to dir and run
truffle test
- if this does not all run and breaks halfway try a vm on AWS or run test individually
truffle test test\<filename>
A quick note on wording:
-
"a / the deposit" refers to the value that the contract requires from entering vehicles.
-
"what is deposited" means what was actually sent by the vehicle upon entering.
-
Scenario 1:
vehicle1
enters atbooth1
and deposits required amount (say 10).vehicle1
exits atbooth2
, which route price happens to equal the deposit amount (so 10).vehicle1
gets no refund.
-
Scenario 2:
vehicle1
enters atbooth1
and deposits required amount (say 10).vehicle1
exits atbooth2
, which route price happens to be more than the deposit amount (say 15).vehicle1
gets no refund.
-
Scenario 3:
vehicle1
enters atbooth1
and deposits required amount (say 10).vehicle1
exits atbooth2
, which route price happens to be less than the deposit amount (say 6).vehicle1
gets refunded the difference (so 4).
-
Scenario 4:
vehicle1
enters atbooth1
and deposits (say 14) more than the required amount (say 10).vehicle1
exits atbooth2
, which route price happens to equal the deposit amount (so 10).vehicle1
gets refunded the difference (so 4).
-
Scenario 5:
vehicle1
enters atbooth1
and deposits (say 14) more than the required amount (say 10).vehicle1
exits atbooth2
, which route price happens to be unknown.- the operator's owner updates the route price, which happens to be less than the deposited amount (say 11).
vehicle1
gets refunded the difference (so 3).
-
Scenario 6:
vehicle1
enters atbooth1
and deposits more (say 14) than the required amount (say 10).vehicle1
exits atbooth2
, which route price happens to be unknown.vehicle2
enters atbooth1
and deposits the exact required amount (so 10).vehicle2
exits atbooth2
, which route price happens to be unknown.- the operator's owner updates the route price, which happens to be less than the required deposit (so 6).
vehicle1
gets refunded the difference (so 8).- someone (anyone) calls to clear one pending payment.
vehicle2
gets refunded the difference (so 4).
"sender doesn't have enough funds to send tx. The upfront cost is: 3000000 and the sender's account only has: 0"
restart ganache-cli
and start again
"out of gas"
start ganache-cli
with -l 1000000000
any other weird or not working problems with revert, try a VM on AWS