├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── core ├── build.gradle └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── consensys │ │ │ └── wittgenstein │ │ │ ├── core │ │ │ ├── Block.java │ │ │ ├── BlockChainNetwork.java │ │ │ ├── BlockChainNode.java │ │ │ ├── Envelope.java │ │ │ ├── EnvelopeInfo.java │ │ │ ├── External.java │ │ │ ├── Network.java │ │ │ ├── NetworkLatency.java │ │ │ ├── NetworkThroughput.java │ │ │ ├── Node.java │ │ │ ├── NodeBuilder.java │ │ │ ├── P2PNetwork.java │ │ │ ├── P2PNode.java │ │ │ ├── ProgressPerTime.java │ │ │ ├── Protocol.java │ │ │ ├── RegistryNetworkLatencies.java │ │ │ ├── RegistryNodeBuilders.java │ │ │ ├── RunMultipleTimes.java │ │ │ ├── WParameters.java │ │ │ ├── geoinfo │ │ │ │ ├── CityInfo.java │ │ │ │ ├── Geo.java │ │ │ │ ├── GeoAWS.java │ │ │ │ └── GeoAllCities.java │ │ │ ├── json │ │ │ │ ├── ExternalConverter.java │ │ │ │ ├── ListNodeConverter.java │ │ │ │ └── NodeConverter.java │ │ │ ├── messages │ │ │ │ ├── ConditionalTask.java │ │ │ │ ├── FloodMessage.java │ │ │ │ ├── Message.java │ │ │ │ ├── PeriodicTask.java │ │ │ │ ├── SendMessage.java │ │ │ │ ├── StatusFloodMessage.java │ │ │ │ └── Task.java │ │ │ └── utils │ │ │ │ ├── BitSetUtils.java │ │ │ │ ├── GeneralizedParetoDistribution.java │ │ │ │ ├── Kademlia.java │ │ │ │ ├── MoreMath.java │ │ │ │ ├── Reflects.java │ │ │ │ ├── StatsHelper.java │ │ │ │ └── Strings.java │ │ │ └── tools │ │ │ ├── CSVFormatter.java │ │ │ ├── CSVLatencyReader.java │ │ │ ├── GifSequenceWriter.java │ │ │ ├── Graph.java │ │ │ └── NodeDrawer.java │ └── resources │ │ ├── Data │ │ ├── Adelaide │ │ │ ├── Adelaide.png │ │ │ └── AdelaidePing.csv │ │ ├── Albany │ │ │ ├── Albany.png │ │ │ └── AlbanyPing.csv │ │ ├── Alblasserdam │ │ │ ├── Alblasserdam.png │ │ │ └── AlblasserdamPing.csv │ │ ├── Albuquerque │ │ │ ├── Albuquerque.png │ │ │ └── AlbuquerquePing.csv │ │ ├── Algiers │ │ │ ├── Algiers.png │ │ │ └── AlgiersPing.csv │ │ ├── Amsterdam │ │ │ ├── Amsterdam.png │ │ │ └── AmsterdamPing.csv │ │ ├── Anchorage │ │ │ ├── Anchorage.png │ │ │ └── AnchoragePing.csv │ │ ├── Ankara │ │ │ ├── Ankara.png │ │ │ └── AnkaraPing.csv │ │ ├── Antwerp │ │ │ ├── Antwerp.png │ │ │ └── AntwerpPing.csv │ │ ├── Arezzo │ │ │ ├── Arezzo.png │ │ │ └── ArezzoPing.csv │ │ ├── Asheville │ │ │ ├── Asheville.png │ │ │ └── AshevillePing.csv │ │ ├── Athens │ │ │ ├── Athens.png │ │ │ └── AthensPing.csv │ │ ├── Atlanta │ │ │ ├── Atlanta.png │ │ │ └── AtlantaPing.csv │ │ ├── Auckland │ │ │ ├── Auckland.png │ │ │ └── AucklandPing.csv │ │ ├── Austin │ │ │ ├── Austin.png │ │ │ └── AustinPing.csv │ │ ├── Baltimore │ │ │ ├── Baltimore.png │ │ │ └── BaltimorePing.csv │ │ ├── Bangalore │ │ │ ├── Bangalore.png │ │ │ └── BangalorePing.csv │ │ ├── Bangkok │ │ │ ├── Bangkok.png │ │ │ └── BangkokPing.csv │ │ ├── Barcelona │ │ │ ├── Barcelona.png │ │ │ └── BarcelonaPing.csv │ │ ├── Basel │ │ │ ├── Basel.png │ │ │ └── BaselPing.csv │ │ ├── Belfast │ │ │ ├── Belfast.png │ │ │ └── BelfastPing.csv │ │ ├── Bergen │ │ │ ├── Bergen.png │ │ │ └── BergenPing.csv │ │ ├── Berkeley+Springs │ │ │ ├── Berkeley+Springs.png │ │ │ └── Berkeley+SpringsPing.csv │ │ ├── Bern │ │ │ ├── Bern.png │ │ │ └── BernPing.csv │ │ ├── Bogota │ │ │ ├── Bogota.png │ │ │ └── BogotaPing.csv │ │ ├── Boston │ │ │ ├── Boston.png │ │ │ └── BostonPing.csv │ │ ├── Brasilia │ │ │ ├── Brasilia.png │ │ │ └── BrasiliaPing.csv │ │ ├── Bratislava │ │ │ ├── Bratislava.png │ │ │ └── BratislavaPing.csv │ │ ├── Brisbane │ │ │ ├── Brisbane.png │ │ │ └── BrisbanePing.csv │ │ ├── Bristol │ │ │ ├── Bristol.png │ │ │ └── BristolPing.csv │ │ ├── Brno │ │ │ ├── Brno.png │ │ │ └── BrnoPing.csv │ │ ├── Bruges │ │ │ ├── Bruges.png │ │ │ └── BrugesPing.csv │ │ ├── Brunswick │ │ │ ├── Brunswick.png │ │ │ └── BrunswickPing.csv │ │ ├── Brussels │ │ │ ├── Brussels.png │ │ │ └── BrusselsPing.csv │ │ ├── Bucharest │ │ │ ├── Bucharest.png │ │ │ └── BucharestPing.csv │ │ ├── Budapest │ │ │ ├── Budapest.png │ │ │ └── BudapestPing.csv │ │ ├── Buenos+Aires │ │ │ ├── Buenos+Aires.png │ │ │ └── Buenos+AiresPing.csv │ │ ├── Buffalo │ │ │ ├── Buffalo.png │ │ │ └── BuffaloPing.csv │ │ ├── Bursa │ │ │ ├── Bursa.png │ │ │ └── BursaPing.csv │ │ ├── Cairo │ │ │ ├── Cairo.png │ │ │ └── CairoPing.csv │ │ ├── Calgary │ │ │ ├── Calgary.png │ │ │ └── CalgaryPing.csv │ │ ├── Canberra │ │ │ ├── Canberra.png │ │ │ └── CanberraPing.csv │ │ ├── Cape+Town │ │ │ ├── Cape+Town.png │ │ │ └── Cape+TownPing.csv │ │ ├── Caracas │ │ │ ├── Caracas.png │ │ │ └── CaracasPing.csv │ │ ├── Cardiff │ │ │ ├── Cardiff.png │ │ │ └── CardiffPing.csv │ │ ├── Charlotte │ │ │ ├── Charlotte.png │ │ │ └── CharlottePing.csv │ │ ├── Cheltenham │ │ │ ├── Cheltenham.png │ │ │ └── CheltenhamPing.csv │ │ ├── Chennai │ │ │ ├── Chennai.png │ │ │ └── ChennaiPing.csv │ │ ├── Chicago │ │ │ ├── Chicago.png │ │ │ └── ChicagoPing.csv │ │ ├── Chisinau │ │ │ ├── Chisinau.png │ │ │ └── ChisinauPing.csv │ │ ├── Christchurch │ │ │ ├── Christchurch.png │ │ │ └── ChristchurchPing.csv │ │ ├── Cincinnati │ │ │ ├── Cincinnati.png │ │ │ └── CincinnatiPing.csv │ │ ├── Cleveland │ │ │ ├── Cleveland.png │ │ │ └── ClevelandPing.csv │ │ ├── Colorado+Springs │ │ │ ├── Colorado+Springs.png │ │ │ └── Colorado+SpringsPing.csv │ │ ├── Columbus │ │ │ ├── Columbus.png │ │ │ └── ColumbusPing.csv │ │ ├── Copenhagen │ │ │ ├── Copenhagen.png │ │ │ └── CopenhagenPing.csv │ │ ├── Coventry │ │ │ ├── Coventry.png │ │ │ └── CoventryPing.csv │ │ ├── Cromwell │ │ │ ├── Cromwell.png │ │ │ └── CromwellPing.csv │ │ ├── Dagupan │ │ │ ├── Dagupan.png │ │ │ └── DagupanPing.csv │ │ ├── Dallas │ │ │ ├── Dallas.png │ │ │ └── DallasPing.csv │ │ ├── Dar+es+Salaam │ │ │ ├── Dar+es+Salaam.png │ │ │ └── Dar+es+SalaamPing.csv │ │ ├── Denver │ │ │ ├── Denver.png │ │ │ └── DenverPing.csv │ │ ├── Des+Moines │ │ │ ├── Des+Moines.png │ │ │ └── Des+MoinesPing.csv │ │ ├── Detroit │ │ │ ├── Detroit.png │ │ │ └── DetroitPing.csv │ │ ├── Dhaka │ │ │ ├── Dhaka.png │ │ │ └── DhakaPing.csv │ │ ├── Dronten │ │ │ ├── Dronten.png │ │ │ └── DrontenPing.csv │ │ ├── Dubai │ │ │ ├── Dubai.png │ │ │ └── DubaiPing.csv │ │ ├── Dublin │ │ │ ├── Dublin.png │ │ │ └── DublinPing.csv │ │ ├── Dusseldorf │ │ │ ├── Dusseldorf.png │ │ │ └── DusseldorfPing.csv │ │ ├── Edinburgh │ │ │ ├── Edinburgh.png │ │ │ └── EdinburghPing.csv │ │ ├── Eindhoven │ │ │ ├── Eindhoven.png │ │ │ └── EindhovenPing.csv │ │ ├── Falkenstein │ │ │ ├── Falkenstein.png │ │ │ └── FalkensteinPing.csv │ │ ├── Fez │ │ │ ├── Fez.png │ │ │ └── FezPing.csv │ │ ├── Frankfurt │ │ │ ├── Frankfurt.png │ │ │ └── FrankfurtPing.csv │ │ ├── Fremont │ │ │ ├── Fremont.png │ │ │ └── FremontPing.csv │ │ ├── Frosinone │ │ │ ├── Frosinone.png │ │ │ └── FrosinonePing.csv │ │ ├── Gdansk │ │ │ ├── Gdansk.png │ │ │ └── GdanskPing.csv │ │ ├── Geneva │ │ │ ├── Geneva.png │ │ │ └── GenevaPing.csv │ │ ├── Gothenburg │ │ │ ├── Gothenburg.png │ │ │ └── GothenburgPing.csv │ │ ├── Green+Bay │ │ │ ├── Green+Bay.png │ │ │ └── Green+BayPing.csv │ │ ├── Groningen │ │ │ ├── Groningen.png │ │ │ └── GroningenPing.csv │ │ ├── Halifax │ │ │ ├── Halifax.png │ │ │ └── HalifaxPing.csv │ │ ├── Hamburg │ │ │ ├── Hamburg.png │ │ │ └── HamburgPing.csv │ │ ├── Hangzhou │ │ │ ├── Hangzhou.png │ │ │ └── HangzhouPing.csv │ │ ├── Hanoi │ │ │ ├── Hanoi.png │ │ │ └── HanoiPing.csv │ │ ├── Helsinki │ │ │ ├── Helsinki.png │ │ │ └── HelsinkiPing.csv │ │ ├── Heredia │ │ │ ├── Heredia.png │ │ │ └── HerediaPing.csv │ │ ├── Ho+Chi+Minh+City │ │ │ ├── Ho+Chi+Minh+City.png │ │ │ └── Ho+Chi+Minh+CityPing.csv │ │ ├── Hong+Kong │ │ │ ├── Hong+Kong.png │ │ │ └── Hong+KongPing.csv │ │ ├── Honolulu │ │ │ ├── Honolulu.png │ │ │ └── HonoluluPing.csv │ │ ├── Houston │ │ │ ├── Houston.png │ │ │ └── HoustonPing.csv │ │ ├── Hyderabad │ │ │ ├── Hyderabad.png │ │ │ └── HyderabadPing.csv │ │ ├── Indianapolis │ │ │ ├── Indianapolis.png │ │ │ └── IndianapolisPing.csv │ │ ├── Indore │ │ │ ├── Indore.png │ │ │ └── IndorePing.csv │ │ ├── Istanbul │ │ │ ├── Istanbul.png │ │ │ └── IstanbulPing.csv │ │ ├── Izmir │ │ │ ├── Izmir.png │ │ │ └── IzmirPing.csv │ │ ├── Jackson │ │ │ ├── Jackson.png │ │ │ └── JacksonPing.csv │ │ ├── Jacksonville │ │ │ ├── Jacksonville.png │ │ │ └── JacksonvillePing.csv │ │ ├── Jakarta │ │ │ ├── Jakarta.png │ │ │ └── JakartaPing.csv │ │ ├── Jerusalem │ │ │ ├── Jerusalem.png │ │ │ └── JerusalemPing.csv │ │ ├── Joao+Pessoa │ │ │ ├── Joao+Pessoa.png │ │ │ └── Joao+PessoaPing.csv │ │ ├── Johannesburg │ │ │ ├── Johannesburg.png │ │ │ └── JohannesburgPing.csv │ │ ├── Kampala │ │ │ ├── Kampala.png │ │ │ └── KampalaPing.csv │ │ ├── Kansas+City │ │ │ ├── Kansas+City.png │ │ │ └── Kansas+CityPing.csv │ │ ├── Karaganda │ │ │ ├── Karaganda.png │ │ │ └── KaragandaPing.csv │ │ ├── Kiev │ │ │ ├── Kiev.png │ │ │ └── KievPing.csv │ │ ├── Knoxville │ │ │ ├── Knoxville.png │ │ │ └── KnoxvillePing.csv │ │ ├── Koto │ │ │ ├── Koto.png │ │ │ └── KotoPing.csv │ │ ├── Ktis │ │ │ ├── Ktis.png │ │ │ └── KtisPing.csv │ │ ├── La+Ceiba │ │ │ ├── La+Ceiba.png │ │ │ └── La+CeibaPing.csv │ │ ├── Lagos │ │ │ ├── Lagos.png │ │ │ └── LagosPing.csv │ │ ├── Lahore │ │ │ ├── Lahore.png │ │ │ └── LahorePing.csv │ │ ├── Las+Vegas │ │ │ ├── Las+Vegas.png │ │ │ └── Las+VegasPing.csv │ │ ├── Lausanne │ │ │ ├── Lausanne.png │ │ │ └── LausannePing.csv │ │ ├── Liege │ │ │ ├── Liege.png │ │ │ └── LiegePing.csv │ │ ├── Lima │ │ │ ├── Lima.png │ │ │ └── LimaPing.csv │ │ ├── Limassol │ │ │ ├── Limassol.png │ │ │ └── LimassolPing.csv │ │ ├── Lincoln │ │ │ ├── Lincoln.png │ │ │ └── LincolnPing.csv │ │ ├── Lisbon │ │ │ ├── Lisbon.png │ │ │ └── LisbonPing.csv │ │ ├── Ljubljana │ │ │ ├── Ljubljana.png │ │ │ └── LjubljanaPing.csv │ │ ├── London │ │ │ ├── London.png │ │ │ └── LondonPing.csv │ │ ├── Los+Angeles │ │ │ ├── Los+Angeles.png │ │ │ └── Los+AngelesPing.csv │ │ ├── Louisville │ │ │ ├── Louisville.png │ │ │ └── LouisvillePing.csv │ │ ├── Lugano │ │ │ ├── Lugano.png │ │ │ └── LuganoPing.csv │ │ ├── Luxembourg │ │ │ ├── Luxembourg.png │ │ │ └── LuxembourgPing.csv │ │ ├── Lyon │ │ │ ├── Lyon.png │ │ │ └── LyonPing.csv │ │ ├── Madrid │ │ │ ├── Madrid.png │ │ │ └── MadridPing.csv │ │ ├── Maidstone │ │ │ ├── Maidstone.png │ │ │ └── MaidstonePing.csv │ │ ├── Malaysia │ │ │ ├── Malaysia.png │ │ │ └── MalaysiaPing.csv │ │ ├── Manchester │ │ │ ├── Manchester.png │ │ │ └── ManchesterPing.csv │ │ ├── Manhattan │ │ │ ├── Manhattan.png │ │ │ └── ManhattanPing.csv │ │ ├── Manila │ │ │ ├── Manila.png │ │ │ └── ManilaPing.csv │ │ ├── Marseille │ │ │ ├── Marseille.png │ │ │ └── MarseillePing.csv │ │ ├── Medellin │ │ │ ├── Medellin.png │ │ │ └── MedellinPing.csv │ │ ├── Melbourne │ │ │ ├── Melbourne.png │ │ │ └── MelbournePing.csv │ │ ├── Memphis │ │ │ ├── Memphis.png │ │ │ └── MemphisPing.csv │ │ ├── Mexico │ │ │ ├── Mexico.png │ │ │ └── MexicoPing.csv │ │ ├── Miami │ │ │ ├── Miami.png │ │ │ └── MiamiPing.csv │ │ ├── Milan │ │ │ ├── Milan.png │ │ │ └── MilanPing.csv │ │ ├── Milwaukee │ │ │ ├── Milwaukee.png │ │ │ └── MilwaukeePing.csv │ │ ├── Minneapolis │ │ │ ├── Minneapolis.png │ │ │ └── MinneapolisPing.csv │ │ ├── Missoula │ │ │ ├── Missoula.png │ │ │ └── MissoulaPing.csv │ │ ├── Montevideo │ │ │ ├── Montevideo.png │ │ │ └── MontevideoPing.csv │ │ ├── Monticello │ │ │ ├── Monticello.png │ │ │ └── MonticelloPing.csv │ │ ├── Montreal │ │ │ ├── Montreal.png │ │ │ └── MontrealPing.csv │ │ ├── Moscow │ │ │ ├── Moscow.png │ │ │ └── MoscowPing.csv │ │ ├── Munich │ │ │ ├── Munich.png │ │ │ └── MunichPing.csv │ │ ├── Nairobi │ │ │ ├── Nairobi.png │ │ │ └── NairobiPing.csv │ │ ├── New+Delhi │ │ │ ├── New+Delhi.png │ │ │ └── New+DelhiPing.csv │ │ ├── New+Orleans │ │ │ ├── New+Orleans.png │ │ │ └── New+OrleansPing.csv │ │ ├── New+York │ │ │ ├── New+York.png │ │ │ └── New+YorkPing.csv │ │ ├── Newcastle │ │ │ ├── Newcastle.png │ │ │ └── NewcastlePing.csv │ │ ├── Nis │ │ │ ├── Nis.png │ │ │ └── NisPing.csv │ │ ├── Novosibirsk │ │ │ ├── Novosibirsk.png │ │ │ └── NovosibirskPing.csv │ │ ├── Nuremberg │ │ │ ├── Nuremberg.png │ │ │ └── NurembergPing.csv │ │ ├── Oklahoma+City │ │ │ ├── Oklahoma+City.png │ │ │ └── Oklahoma+CityPing.csv │ │ ├── Orlando │ │ │ ├── Orlando.png │ │ │ └── OrlandoPing.csv │ │ ├── Osaka │ │ │ ├── Osaka.png │ │ │ └── OsakaPing.csv │ │ ├── Oslo │ │ │ ├── Oslo.png │ │ │ └── OsloPing.csv │ │ ├── Ottawa │ │ │ ├── Ottawa.png │ │ │ └── OttawaPing.csv │ │ ├── Palermo │ │ │ ├── Palermo.png │ │ │ └── PalermoPing.csv │ │ ├── Panama │ │ │ ├── Panama.png │ │ │ └── PanamaPing.csv │ │ ├── Paramaribo │ │ │ ├── Paramaribo.png │ │ │ └── ParamariboPing.csv │ │ ├── Paris │ │ │ ├── Paris.png │ │ │ └── ParisPing.csv │ │ ├── Perth │ │ │ ├── Perth.png │ │ │ └── PerthPing.csv │ │ ├── Philadelphia │ │ │ ├── Philadelphia.png │ │ │ └── PhiladelphiaPing.csv │ │ ├── Phnom+Penh │ │ │ ├── Phnom+Penh.png │ │ │ └── Phnom+PenhPing.csv │ │ ├── Phoenix │ │ │ ├── Phoenix.png │ │ │ └── PhoenixPing.csv │ │ ├── Piscataway │ │ │ ├── Piscataway.png │ │ │ └── PiscatawayPing.csv │ │ ├── Pittsburgh │ │ │ ├── Pittsburgh.png │ │ │ └── PittsburghPing.csv │ │ ├── Portland │ │ │ ├── Portland.png │ │ │ └── PortlandPing.csv │ │ ├── Prague │ │ │ ├── Prague.png │ │ │ └── PraguePing.csv │ │ ├── Pune │ │ │ ├── Pune.png │ │ │ └── PunePing.csv │ │ ├── Quebec+City │ │ │ ├── Quebec+City.png │ │ │ └── Quebec+CityPing.csv │ │ ├── Quito │ │ │ ├── Quito.png │ │ │ └── QuitoPing.csv │ │ ├── Raleigh │ │ │ ├── Raleigh.png │ │ │ └── RaleighPing.csv │ │ ├── Redding │ │ │ ├── Redding.png │ │ │ └── ReddingPing.csv │ │ ├── Reykjavik │ │ │ ├── Reykjavik.png │ │ │ └── ReykjavikPing.csv │ │ ├── Richmond │ │ │ ├── Richmond.png │ │ │ └── RichmondPing.csv │ │ ├── Riga │ │ │ ├── Riga.png │ │ │ └── RigaPing.csv │ │ ├── Riyadh │ │ │ ├── Riyadh.png │ │ │ └── RiyadhPing.csv │ │ ├── Rome │ │ │ ├── Rome.png │ │ │ └── RomePing.csv │ │ ├── Roseburg │ │ │ ├── Roseburg.png │ │ │ └── RoseburgPing.csv │ │ ├── Rotterdam │ │ │ ├── Rotterdam.png │ │ │ └── RotterdamPing.csv │ │ ├── Roubaix │ │ │ ├── Roubaix.png │ │ │ └── RoubaixPing.csv │ │ ├── Sacramento │ │ │ ├── Sacramento.png │ │ │ └── SacramentoPing.csv │ │ ├── Salem │ │ │ ├── Salem.png │ │ │ └── SalemPing.csv │ │ ├── Salt+Lake+City │ │ │ ├── Salt+Lake+City.png │ │ │ └── Salt+Lake+CityPing.csv │ │ ├── San+Antonio │ │ │ ├── San+Antonio.png │ │ │ └── San+AntonioPing.csv │ │ ├── San+Diego │ │ │ ├── San+Diego.png │ │ │ └── San+DiegoPing.csv │ │ ├── San+Francisco │ │ │ ├── San+Francisco.png │ │ │ └── San+FranciscoPing.csv │ │ ├── San+Jose │ │ │ ├── San+Jose.png │ │ │ └── San+JosePing.csv │ │ ├── San+Juan │ │ │ ├── San+Juan.png │ │ │ └── San+JuanPing.csv │ │ ├── Santiago │ │ │ ├── Santiago.png │ │ │ └── SantiagoPing.csv │ │ ├── Sao+Paulo │ │ │ ├── Sao+Paulo.png │ │ │ └── Sao+PauloPing.csv │ │ ├── Sapporo │ │ │ ├── Sapporo.png │ │ │ └── SapporoPing.csv │ │ ├── Saskatoon │ │ │ ├── Saskatoon.png │ │ │ └── SaskatoonPing.csv │ │ ├── Savannah │ │ │ ├── Savannah.png │ │ │ └── SavannahPing.csv │ │ ├── Scranton │ │ │ ├── Scranton.png │ │ │ └── ScrantonPing.csv │ │ ├── Seattle │ │ │ ├── Seattle.png │ │ │ └── SeattlePing.csv │ │ ├── Secaucus │ │ │ ├── Secaucus.png │ │ │ └── SecaucusPing.csv │ │ ├── Seoul │ │ │ ├── Seoul.png │ │ │ └── SeoulPing.csv │ │ ├── Shanghai │ │ │ ├── Shanghai.png │ │ │ └── ShanghaiPing.csv │ │ ├── Shenzhen │ │ │ ├── Shenzhen.png │ │ │ └── ShenzhenPing.csv │ │ ├── Singapore │ │ │ ├── Singapore.png │ │ │ └── SingaporePing.csv │ │ ├── Sofia │ │ │ ├── Sofia.png │ │ │ └── SofiaPing.csv │ │ ├── South+Bend │ │ │ ├── South+Bend.png │ │ │ └── South+BendPing.csv │ │ ├── St+Louis │ │ │ ├── St+Louis.png │ │ │ └── St+LouisPing.csv │ │ ├── St+Petersburg │ │ │ ├── St+Petersburg.png │ │ │ └── St+PetersburgPing.csv │ │ ├── Stamford │ │ │ ├── Stamford.png │ │ │ └── StamfordPing.csv │ │ ├── Stockholm │ │ │ ├── Stockholm.png │ │ │ └── StockholmPing.csv │ │ ├── Strasbourg │ │ │ ├── Strasbourg.png │ │ │ └── StrasbourgPing.csv │ │ ├── Sydney │ │ │ ├── Sydney.png │ │ │ └── SydneyPing.csv │ │ ├── Syracuse │ │ │ ├── Syracuse.png │ │ │ └── SyracusePing.csv │ │ ├── Taipei │ │ │ ├── Taipei.png │ │ │ └── TaipeiPing.csv │ │ ├── Tallinn │ │ │ ├── Tallinn.png │ │ │ └── TallinnPing.csv │ │ ├── Tampa │ │ │ ├── Tampa.png │ │ │ └── TampaPing.csv │ │ ├── Tbilisi │ │ │ ├── Tbilisi.png │ │ │ └── TbilisiPing.csv │ │ ├── Tel+Aviv │ │ │ ├── Tel+Aviv.png │ │ │ └── Tel+AvivPing.csv │ │ ├── Tempe │ │ │ ├── Tempe.png │ │ │ └── TempePing.csv │ │ ├── The+Hague │ │ │ ├── The+Hague.png │ │ │ └── The+HaguePing.csv │ │ ├── Thessaloniki │ │ │ ├── Thessaloniki.png │ │ │ └── ThessalonikiPing.csv │ │ ├── Tirana │ │ │ ├── Tirana.png │ │ │ └── TiranaPing.csv │ │ ├── Tokyo │ │ │ ├── Tokyo.png │ │ │ └── TokyoPing.csv │ │ ├── Toledo │ │ │ ├── Toledo.png │ │ │ └── ToledoPing.csv │ │ ├── Toronto │ │ │ ├── Toronto.png │ │ │ └── TorontoPing.csv │ │ ├── Tunis │ │ │ ├── Tunis.png │ │ │ └── TunisPing.csv │ │ ├── Valencia │ │ │ ├── Valencia.png │ │ │ └── ValenciaPing.csv │ │ ├── Valletta │ │ │ ├── Valletta.png │ │ │ └── VallettaPing.csv │ │ ├── Vancouver │ │ │ ├── Vancouver.png │ │ │ └── VancouverPing.csv │ │ ├── Varna │ │ │ ├── Varna.png │ │ │ └── VarnaPing.csv │ │ ├── Venice │ │ │ ├── Venice.png │ │ │ └── VenicePing.csv │ │ ├── Vienna │ │ │ ├── Vienna.png │ │ │ └── ViennaPing.csv │ │ ├── Vilnius │ │ │ ├── Vilnius.png │ │ │ └── VilniusPing.csv │ │ ├── Vladivostok │ │ │ ├── Vladivostok.png │ │ │ └── VladivostokPing.csv │ │ ├── Warsaw │ │ │ ├── Warsaw.png │ │ │ └── WarsawPing.csv │ │ ├── Washington │ │ │ ├── Washington.png │ │ │ └── WashingtonPing.csv │ │ ├── Wellington │ │ │ ├── Wellington.png │ │ │ └── WellingtonPing.csv │ │ ├── Westpoort │ │ │ ├── Westpoort.png │ │ │ └── WestpoortPing.csv │ │ ├── Winnipeg │ │ │ ├── Winnipeg.png │ │ │ └── WinnipegPing.csv │ │ ├── Zhangjiakou │ │ │ ├── Zhangjiakou.png │ │ │ └── ZhangjiakouPing.csv │ │ └── Zurich │ │ │ ├── Zurich.png │ │ │ └── ZurichPing.csv │ │ ├── cities.csv │ │ └── world-map-2000px.png │ └── test │ └── java │ └── net │ └── consensys │ └── wittgenstein │ ├── core │ ├── CityPopulationTest.java │ ├── EnvelopeStorageTest.java │ ├── NetworkLatencyTest.java │ ├── NetworkTest.java │ ├── NetworkThroughputTest.java │ ├── NodeBuilderTest.java │ ├── P2PNetworkTest.java │ └── StatsTest.java │ └── tools │ ├── CSVFormatterTest.java │ ├── CSVLatencyReaderTest.java │ ├── GraphTest.java │ └── NodeDrawerTest.java ├── protocols ├── build.gradle └── src │ ├── main │ └── java │ │ └── net │ │ └── consensys │ │ └── wittgenstein │ │ └── protocols │ │ ├── CasperIMD.java │ │ ├── Dfinity.java │ │ ├── ENRGossiping.java │ │ ├── GSFSignature.java │ │ ├── Handel.java │ │ ├── HandelScenarios.java │ │ ├── OptimisticP2PSignature.java │ │ ├── OptimisticP2PSignatureScenarios.java │ │ ├── P2PFlood.java │ │ ├── P2PHandel.java │ │ ├── P2PHandelScenarios.java │ │ ├── Paxos.java │ │ ├── PingPong.java │ │ ├── SanFerminCappos.java │ │ ├── SanFerminHelper.java │ │ ├── SanFerminSignature.java │ │ ├── Slush.java │ │ ├── Snowflake.java │ │ ├── ethpow │ │ ├── ETHAgentMiner.java │ │ ├── ETHMiner.java │ │ ├── ETHMinerAgent.java │ │ ├── ETHPoW.java │ │ ├── ETHSelfishMiner.java │ │ └── ETHSelfishMiner2.java │ │ └── handeleth2 │ │ ├── AggToVerify.java │ │ ├── Attestation.java │ │ ├── HLevel.java │ │ ├── HNode.java │ │ ├── HandelEth2.java │ │ ├── HandelEth2Parameters.java │ │ └── SendAggregation.java │ └── test │ └── java │ └── net │ └── consensys │ └── wittgenstein │ └── protocols │ ├── CasperByzantineTest.java │ ├── CasperIMDTest.java │ ├── DfinityTest.java │ ├── ENRGossipingTest.java │ ├── GSFSignatureTest.java │ ├── HandelTest.java │ ├── OptimisticP2PSignatureTest.java │ ├── P2PFloodTest.java │ ├── P2PHandelTest.java │ ├── PaxosTest.java │ ├── PingPongTest.java │ ├── SanFerminTest.java │ ├── SlushTest.java │ ├── SnowflakeTest.java │ ├── ethpow │ ├── ETHMinerAgentTest.java │ └── EthPoWTest.java │ └── handeleth2 │ └── HandelEth2Test.java ├── settings.gradle └── wserver ├── build.gradle └── src ├── main ├── java │ └── net │ │ └── consensys │ │ └── wittgenstein │ │ └── server │ │ ├── ExternalMockImplementation.java │ │ ├── ExternalRest.java │ │ ├── IServer.java │ │ ├── Server.java │ │ └── ws │ │ ├── ExternalWS.java │ │ ├── ObjectMapperFactory.java │ │ └── WServer.java └── resources │ └── static │ └── index.html └── test └── java └── net └── consensys └── wittgenstein └── server └── ws └── WServerTest.java /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Java Gradle CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-java/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/openjdk:9-jdk 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/postgres:9.4 16 | 17 | working_directory: ~/repo 18 | 19 | environment: 20 | # Customize the JVM maximum heap limit 21 | JVM_OPTS: 22 | GRADLE_OPTS: -Dorg.gradle.daemon=false 23 | TERM: dumb 24 | 25 | steps: 26 | - checkout 27 | 28 | # Download and cache dependencies 29 | - restore_cache: 30 | keys: 31 | - v1-dependencies-{{ checksum "build.gradle" }} 32 | # fallback to using the latest cache if no exact match is found 33 | - v1-dependencies- 34 | 35 | - run: gradle --no-daemon dependencies 36 | 37 | - save_cache: 38 | paths: 39 | - ~/.gradle 40 | key: v1-dependencies-{{ checksum "build.gradle" }} 41 | 42 | # run tests! 43 | - run: gradle spotlessCheck build test --max-workers 1 --no-daemon 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.swp 3 | *.tmp 4 | *~.nib 5 | *.iml 6 | *.launch 7 | *.swp 8 | *.log 9 | .classpath 10 | .DS_Store 11 | .externalToolBuilders/ 12 | .gradle/ 13 | .idea/ 14 | .loadpath 15 | .metadata 16 | .prefs 17 | .project 18 | .recommenders/ 19 | .settings 20 | .springBeans 21 | .vertx 22 | bin/ 23 | local.properties 24 | target/ 25 | tmp/ 26 | build/ 27 | out/ 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/ConsenSys/wittgenstein.svg?style=svg&circle-token=586dc7b883ee5381aa17247b8f058157e166b307)](https://circleci.com/gh/ConsenSys/wittgenstein) 2 | 3 | # Wittgenstein 4 | A simulator for some PoS, consensus or just distributed algorithms. Includes dfinity, Ethereum's Casper IMD, San Fermin and others. A somehow outdated presentation can be found [here](https://docs.google.com/presentation/d/1NQAm_zciVToOD3LpauKPxpkQ96WNh_3UMPMA483bHgM/edit?usp=sharing). 5 | 6 | It allows you to test protocols, and to generate large graph and videos of the protocol in action, such as this one (generated for Handel: aggregation at scale with Byzantine nodes (click on the image to see the video): [![Watch the video](https://img.youtube.com/vi/reQTJF7EFLg/maxresdefault.jpg)](https://youtu.be/reQTJF7EFLg) 7 | 8 | 9 | ## Why this name? 10 | Wittgenstein was a concert pianist. He commissioned Ravel's Piano Concerto for the Left Hand, but changed it: 11 | 'lines taken from the orchestral part and added to the solo, harmonies changed, parts added, bars cut and 12 | at the end a newly created series of great swirling arpeggios in the final cadenza.' 13 | 14 | As it's often what happens with mock protocol implementations, it looked like the right name. 15 | 16 | 17 | ## How to build it 18 | You will need java 9+ and gradle installed. 19 | 20 | To check everything is correct: 21 | ``` 22 | gradle clean test 23 | ``` 24 | You can build a jar with gradle: 25 | ``` 26 | gradle clean shadowJar 27 | ``` 28 | ## How to run it 29 | Once built: 30 | ``` 31 | java -Xms6000m -Xmx12048m -classpath protocols/build/libs/wittgenstein-all.jar net.consensys.wittgenstein.protocols.GSFSignature 32 | ``` 33 | 34 | This command is typically for a 16GB machine. The memory is very important when you want to simulate tens of thousands of nodes. If you have less memory, use lower values for -Xms and -Xmx, and run the simulations with less nodes. 35 | 36 | But you're actually supposed to write code to implement your specific scenarios today. An obvious improvement 37 | would be to be able to define scenarios reusable between protocols. 38 | 39 | ## How to implement a new protocol 40 | 41 | For a complete description go to the wiki. 42 | 43 | Here is an example: 44 | ```java 45 | public class PingPong implements Protocol { 46 | /** 47 | * You need a network. Nodes are added to this network. Network latency can be set later. 48 | */ 49 | private final Network network = new Network<>(); 50 | 51 | /** 52 | * Nodes have positions. This position is chosen by the builder. 53 | */ 54 | private final Node.NodeBuilder nb = new Node.NodeBuilderWithRandomPosition(); 55 | 56 | /** 57 | * Messages, exchanged on the network, are specific to the protocol. 58 | */ 59 | static class Ping extends Network.Message { 60 | @Override 61 | public void action(PingPongNode from, PingPongNode to) { 62 | to.onPing(from); 63 | } 64 | } 65 | 66 | static class Pong extends Network.Message { 67 | @Override 68 | public void action(PingPongNode from, PingPongNode to) { 69 | to.onPong(); 70 | } 71 | } 72 | 73 | 74 | class PingPongNode extends Node { 75 | int pong; 76 | 77 | PingPongNode() { 78 | super(network.rd, nb); 79 | } 80 | 81 | void onPing(PingPongNode from) { 82 | network.send(new Pong(), this, from); 83 | } 84 | 85 | void onPong() { 86 | pong++; 87 | } 88 | } 89 | 90 | @Override 91 | public PingPong copy() { 92 | return new PingPong(); 93 | } 94 | 95 | @Override 96 | public void init() { 97 | for (int i = 0; i < 1000; i++) { 98 | network.addNode(new PingPongNode()); 99 | } 100 | } 101 | 102 | @Override 103 | public Network network() { 104 | return network; 105 | } 106 | 107 | 108 | public static void main(String... args) { 109 | PingPong p = new PingPong(); 110 | 111 | p.network.setNetworkLatency(new NetworkLatency.NetworkLatencyByDistance()); 112 | 113 | p.init(); 114 | PingPongNode witness = p.network.getNodeById(0); 115 | p.network.sendAll(new Ping(), witness); 116 | for (int i = 0; i < 1000; i += 100) { 117 | System.out.println(i + " ms, pongs received " + witness.pong); 118 | p.network.runMs(100); 119 | } 120 | } 121 | } 122 | ``` 123 | This will print: 124 | ``` 125 | 0 ms, pongs received 0 126 | 100 ms, pongs received 38 127 | 200 ms, pongs received 184 128 | 300 ms, pongs received 420 129 | 400 ms, pongs received 765 130 | 500 ms, pongs received 969 131 | 600 ms, pongs received 998 132 | 700 ms, pongs received 1000 133 | 800 ms, pongs received 1000 134 | 900 ms, pongs received 1000 135 | ``` 136 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.diffplug.gradle.spotless" version "3.23.1" 3 | id "com.github.johnrengelman.shadow" version "4.0.4" 4 | } 5 | 6 | subprojects { 7 | apply plugin: 'java' 8 | sourceCompatibility = 1.9 9 | targetCompatibility = 1.9 10 | 11 | apply plugin: 'maven' 12 | apply plugin: 'com.diffplug.gradle.spotless' 13 | 14 | repositories { 15 | mavenLocal() 16 | maven { url "https://repo.maven.apache.org/maven2" } 17 | } 18 | 19 | dependencies { 20 | implementation "org.springframework.boot:spring-boot-starter-web:2.1.9.RELEASE" 21 | implementation 'com.google.guava:guava:28.1-jre' 22 | 23 | testImplementation 'junit:junit:4.12' 24 | } 25 | 26 | spotless { 27 | java { 28 | removeUnusedImports() 29 | googleJavaFormat('1.7') 30 | trimTrailingWhitespace() 31 | endWithNewline() 32 | } 33 | } 34 | 35 | test { 36 | environment "GROOVY_TURN_OFF_JAVA_WARNINGS", "true" 37 | // To use less memory during the tests, for the execution on circle ci... 38 | jvmArgs "-XX:+UseSerialGC", "-Xss512k", "-Dspring.test.context.cache.maxSize=1" 39 | // We need to limit the memory to be on par with CircleCI 40 | // It seems that 2g is the maximum we can get on CircleCI 41 | maxHeapSize = "2400m" 42 | afterSuite { desc, result -> 43 | if (!desc.parent) 44 | println("${result.resultType} " + 45 | "(${result.testCount} tests, " + 46 | "${result.successfulTestCount} successes, " + 47 | "${result.failedTestCount} failures, " + 48 | "${result.skippedTestCount} skipped)") 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | dependencies { 3 | implementation 'org.knowm.xchart:xchart:3.5.2' 4 | implementation "org.apache.commons:commons-csv:1.5" 5 | } 6 | 7 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/Block.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | @SuppressWarnings("WeakerAccess") 4 | public abstract class Block { 5 | 6 | /** 7 | * To ensure that all blocks id are unique we increment a counter. We suppose it's impossible to 8 | * create two blocks with the same id. 9 | */ 10 | private static long blockId = 1; 11 | 12 | public final int height; 13 | public final int proposalTime; 14 | public final long lastTxId; 15 | public final long id; 16 | public final TB parent; 17 | public final BlockChainNode producer; 18 | 19 | public final boolean valid; 20 | 21 | /** To create a genesis block... */ 22 | public Block(int h) { 23 | height = h; 24 | lastTxId = 0; 25 | id = 0; 26 | parent = null; 27 | producer = null; 28 | proposalTime = 0; 29 | valid = true; 30 | } 31 | 32 | public static long getLastBlockId() { 33 | return blockId; 34 | } 35 | 36 | public Block(BlockChainNode producer, int height, TB parent, boolean valid, int time) { 37 | if (height <= 0) { 38 | throw new IllegalArgumentException("Only the genesis block has a special height"); 39 | } 40 | if (parent != null && time < parent.proposalTime) { 41 | throw new IllegalArgumentException("bad time: parent is (" + parent + "), our time:" + time); 42 | } 43 | if (parent != null && parent.height >= height) { 44 | throw new IllegalArgumentException("Bad parent. me:" + this + ", parent:" + parent); 45 | } 46 | 47 | this.producer = producer; 48 | this.height = height; 49 | this.id = blockId++; 50 | this.parent = parent; 51 | this.valid = valid; 52 | this.lastTxId = time; 53 | this.proposalTime = time; 54 | } 55 | 56 | /** @return the number of transactions in this block. */ 57 | public long txCount() { 58 | if (id == 0) return 0; 59 | assert parent != null; 60 | 61 | long res = lastTxId - parent.lastTxId; 62 | if (res < 0) { 63 | throw new IllegalStateException(this + ", bad txCount:" + res); 64 | } 65 | return res; 66 | } 67 | 68 | @SuppressWarnings("unused") 69 | public boolean isAncestor(Block b) { 70 | if (this == b) return false; 71 | 72 | Block cur = b; 73 | while (cur.height > this.height) { 74 | cur = cur.parent; 75 | assert cur != null; 76 | } 77 | 78 | return (cur == this); 79 | } 80 | 81 | /** 82 | * * 83 | * 84 | * @return true if b is a direct father or ancestor. false if 'b' is on a different branch 85 | */ 86 | public boolean hasDirectLink(TB b) { 87 | if (b == this) return true; 88 | if (b.height == height) return false; 89 | 90 | Block older = height > b.height ? this : b; 91 | Block young = height < b.height ? this : b; 92 | 93 | while (older.height > young.height) { 94 | older = older.parent; 95 | assert older != null; 96 | } 97 | 98 | return older == young; 99 | } 100 | 101 | @Override 102 | public String toString() { 103 | if (id == 0) { 104 | return "genesis"; 105 | } 106 | 107 | return "h:" 108 | + height 109 | + ", id=" 110 | + id 111 | + ", creationTime:" 112 | + proposalTime 113 | + ", producer=" 114 | + (producer != null ? "" + producer.nodeId : "null") 115 | + ", parent:" 116 | + (parent != null ? "" + parent.id : "null"); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/BlockChainNetwork.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.*; 4 | import net.consensys.wittgenstein.core.messages.Message; 5 | 6 | /** 7 | * Adds some concept to a standard network: it's about to send blocks between nodes. Blockchain 8 | * nodes have a head, a method to choose between blocks and so on. 9 | */ 10 | @SuppressWarnings({"SameParameterValue", "FieldCanBeLocal", "unused"}) 11 | public class BlockChainNetwork> 12 | extends Network { 13 | /** The node we use as an observer for the final stats */ 14 | public TN observer; 15 | 16 | public void addObserver(TN observer) { 17 | this.observer = observer; 18 | addNode(observer); 19 | } 20 | 21 | public static class SendBlock> 22 | extends Message { 23 | final TB toSend; 24 | 25 | public SendBlock(TB toSend) { 26 | this.toSend = toSend; 27 | } 28 | 29 | @Override 30 | public void action(Network network, TN fromNode, TN toNode) { 31 | toNode.onBlock(toSend); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "SendBlock{" + "toSend=" + toSend.id + '}'; 37 | } 38 | } 39 | 40 | /** 41 | * On a blockchain network all the blocks are exchanged all the time. We simulate this with a full 42 | * resent after each partition. 43 | */ 44 | @Override 45 | public void endPartition() { 46 | super.endPartition(); 47 | 48 | for (TN n : allNodes) { 49 | SendBlock sb = new BlockChainNetwork.SendBlock<>(n.head); 50 | sendAll(sb, n); 51 | } 52 | } 53 | 54 | public void printStat(boolean small) { 55 | HashMap> productionCount = new HashMap<>(); 56 | Set> blockProducers = new HashSet<>(); 57 | 58 | Block cur = observer.head; 59 | 60 | int blockInChain = 0; 61 | while (cur != observer.genesis) { 62 | assert cur != null; 63 | assert cur.producer != null; 64 | if (!small) System.out.println("block: " + cur.toString()); 65 | blockInChain++; 66 | 67 | productionCount.putIfAbsent(cur.producer.nodeId, new HashSet<>()); 68 | productionCount.get(cur.producer.nodeId).add(cur); 69 | blockProducers.add(cur.producer); 70 | 71 | cur = cur.parent; 72 | } 73 | 74 | if (!small) { 75 | System.out.println( 76 | "block count:" 77 | + blockInChain 78 | + " on " 79 | + Block.getLastBlockId() 80 | + ", all tx: " 81 | + observer.head.lastTxId); 82 | } 83 | List bps = new ArrayList<>(blockProducers); 84 | bps.sort(Comparator.comparingInt(o -> o.nodeId)); 85 | 86 | for (BlockChainNode bp : bps) { 87 | int bpTx = 0; 88 | for (Block b : productionCount.get(bp.nodeId)) { 89 | bpTx += b.txCount(); 90 | } 91 | if (!small | bp.byzantine) { 92 | System.out.println( 93 | bp 94 | + "; " 95 | + productionCount.get(bp.nodeId).size() 96 | + "; " 97 | + bpTx 98 | + "; " 99 | + bp.msgSent 100 | + "; " 101 | + bp.msgReceived); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/BlockChainNode.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.*; 4 | 5 | @SuppressWarnings("WeakerAccess") 6 | public abstract class BlockChainNode extends Node { 7 | protected final TB genesis; 8 | 9 | /** We keep some data that should be useful for all implementations */ 10 | protected final Map blocksReceivedByBlockId = new HashMap<>(); 11 | 12 | public final Map> blocksReceivedByFatherId = new HashMap<>(); 13 | public final Map> blocksReceivedByHeight = new HashMap<>(); 14 | 15 | public TB head; 16 | 17 | public BlockChainNode(Random rd, NodeBuilder nb, boolean byzantine, TB genesis) { 18 | super(rd, nb, byzantine); 19 | this.genesis = genesis; 20 | this.head = genesis; 21 | this.blocksReceivedByBlockId.put(genesis.id, genesis); 22 | } 23 | 24 | /** 25 | * @return true if it's a new block, false if the block in invalid or if we have already received 26 | * it. 27 | */ 28 | public boolean onBlock(TB b) { 29 | if (!b.valid) { 30 | return false; 31 | } 32 | 33 | if (this.blocksReceivedByBlockId.put(b.id, b) != null) { 34 | return false; // If we have already received this block 35 | } 36 | Set pa = this.blocksReceivedByFatherId.computeIfAbsent(b.parent.id, k -> new HashSet<>()); 37 | pa.add(b); 38 | Set ub = this.blocksReceivedByHeight.computeIfAbsent(b.height, k -> new HashSet<>()); 39 | ub.add(b); 40 | head = best(head, b); 41 | 42 | return true; 43 | } 44 | 45 | /** This describes how to choose the head between two blocks. */ 46 | public abstract TB best(TB cur, TB alt); 47 | 48 | /** 49 | * Counts the transactions included in a block created by this node in the chain starting at this 50 | * head. 51 | */ 52 | public int txsCreatedInChain(Block head) { 53 | int txs = 0; 54 | Block cur = head; 55 | while (cur != null) { 56 | if (cur.producer == this) { 57 | txs += cur.txCount(); 58 | } 59 | cur = cur.parent; 60 | } 61 | return txs; 62 | } 63 | 64 | /** Counts the number of blocks created by this node in the chain starting at this head. */ 65 | public int blocksCreatedInChain(Block head) { 66 | int blocks = 0; 67 | Block cur = head; 68 | while (cur != null) { 69 | if (cur.producer == this) { 70 | blocks++; 71 | } 72 | cur = cur.parent; 73 | } 74 | return blocks; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/EnvelopeInfo.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import net.consensys.wittgenstein.core.messages.Message; 4 | import net.consensys.wittgenstein.core.utils.Strings; 5 | 6 | @SuppressWarnings("WeakerAccess") 7 | public class EnvelopeInfo implements Comparable { 8 | public final int from; 9 | public final int to; 10 | public final int sentAt; 11 | public final int arrivingAt; 12 | public final Message msg; 13 | 14 | EnvelopeInfo(int from, int to, int sentAt, int arrivingAt, Message msg) { 15 | this.from = from; 16 | this.to = to; 17 | this.sentAt = sentAt; 18 | this.arrivingAt = arrivingAt; 19 | this.msg = msg; 20 | } 21 | 22 | // for json 23 | public EnvelopeInfo() { 24 | this(0, 0, 0, 0, null); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return Strings.toString(this); 30 | } 31 | 32 | @Override 33 | public int compareTo(EnvelopeInfo o) { 34 | if (arrivingAt != o.arrivingAt) { 35 | return Integer.compare(arrivingAt, o.arrivingAt); 36 | } 37 | if (sentAt != o.sentAt) { 38 | return Integer.compare(arrivingAt, o.arrivingAt); 39 | } 40 | if (from != o.from) { 41 | return Integer.compare(from, o.from); 42 | } 43 | if (to != o.to) { 44 | return Integer.compare(arrivingAt, o.arrivingAt); 45 | } 46 | return Integer.compare(System.identityHashCode(msg), System.identityHashCode(o.msg)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/External.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.List; 4 | import net.consensys.wittgenstein.core.messages.SendMessage; 5 | 6 | /** Rest API to be implemented by a remote service */ 7 | public interface External { 8 | 9 | List receive(EnvelopeInfo ei); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/NetworkThroughput.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | /** 4 | * Calculates the network throughput given the latency. It's an optimistic evaluation as we don't 5 | * take into account the overlapping messages. 6 | */ 7 | public abstract class NetworkThroughput { 8 | public abstract int delay(Node from, Node to, int delta, int msgSize); 9 | 10 | /** 11 | * Use the equation given by: 12 | * https://cseweb.ucsd.edu/classes/wi01/cse222/papers/mathis-tcpmodel-ccr97.pdf 13 | * 14 | *

There are more complicated ones but this one is commonly used. See 15 | * https://www.netcraftsmen.com/tcp-performance-and-the-mathis-equation/ 16 | * https://www.slac.stanford.edu/comp/net/wan-mon/thru-vs-loss.html 17 | */ 18 | public static class MathisNetworkThroughput extends NetworkThroughput { 19 | private final NetworkLatency nl; 20 | private final int mss = 1460; // maximum segment size 21 | private final int windowSize; 22 | 23 | /** 24 | * Using the optimistic numbers here. It's rather imprecise, because most of the loss come from 25 | * bursts. Note that the paper proposes to use a Pareto distribution. 26 | * https://pdfs.semanticscholar.org/297a/0cc3af443542710d69010676918a0271f2e3.pdf 27 | */ 28 | private final double loss = 0.004; 29 | 30 | private double div = Math.sqrt(loss); 31 | 32 | public MathisNetworkThroughput(NetworkLatency nl) { 33 | this(nl, 87380 * 1024); // 87380; // default on linux 34 | } 35 | 36 | public MathisNetworkThroughput(NetworkLatency nl, int windowSizeBytes) { 37 | this.nl = nl; 38 | this.windowSize = 8 * windowSizeBytes; 39 | } 40 | 41 | /** Mathis: rate < (MSS/RTT)*(1/sqrt(Loss)) */ 42 | @Override 43 | public int delay(Node from, Node to, int delta, int msgSize) { 44 | int st = nl.getLatency(from, to, delta); 45 | 46 | if (msgSize < mss) { 47 | return st; 48 | } 49 | 50 | double rtt = st * 2; 51 | double bandwith = (mss * 8) / (rtt * div); 52 | double wMax = windowSize / rtt; 53 | double avBandwith = Math.min(bandwith, wMax); 54 | 55 | return (int) ((8 * msgSize) / avBandwith + st); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/P2PNetwork.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.*; 4 | import net.consensys.wittgenstein.core.messages.FloodMessage; 5 | 6 | /** 7 | * A peer-to-peer network, i.e. network with nodes interconnected with a small number of peers 8 | * (typically 3-100). On top of this it's possible to add a protocol (gossip, kademlia, ...) to 9 | * exchange messages. 10 | */ 11 | public class P2PNetwork> extends Network { 12 | private final int connectionCount; 13 | private final boolean minimum; 14 | private Set existingLinks = new HashSet<>(); 15 | 16 | /** 17 | * @param connectionCount - the target for the number of connection 18 | * @param minimum - if true, connectionCount is the minimum number of connections per node. If 19 | * false, it's the average number of nodes, with a minimum of 3 connections per node. 20 | */ 21 | public P2PNetwork(int connectionCount, boolean minimum) { 22 | this.connectionCount = connectionCount; 23 | this.minimum = minimum; 24 | } 25 | 26 | public void setPeers() { 27 | if (connectionCount >= allNodes.size()) { 28 | throw new IllegalArgumentException( 29 | "Wrong configuration: #nodes=" 30 | + allNodes.size() 31 | + ", connection target=" 32 | + connectionCount); 33 | } 34 | 35 | if (!minimum) { 36 | int toCreate = (allNodes.size() * connectionCount) / 2; 37 | while (toCreate != existingLinks.size()) { 38 | int pp1 = rd.nextInt(allNodes.size()); 39 | int pp2 = rd.nextInt(allNodes.size()); 40 | createLink(existingLinks, pp1, pp2); 41 | } 42 | } 43 | 44 | // We need to go through the list in a random order, if not we can 45 | // have some side effects if all the dead nodes are at the beginning for example. 46 | // For this reason we work on a shuffled version of the node list. 47 | ArrayList an = new ArrayList<>(allNodes); 48 | Collections.shuffle(an, rd); 49 | for (TN n : an) { 50 | while (n.peers.size() < (minimum ? connectionCount : Math.min(3, this.connectionCount))) { 51 | int pp2 = rd.nextInt(allNodes.size()); 52 | createLink(existingLinks, n.nodeId, pp2); 53 | } 54 | } 55 | } 56 | 57 | public void disconnect(TN p) { 58 | for (TN n : new ArrayList<>(p.peers)) { 59 | removeLink(p, n); 60 | } 61 | } 62 | 63 | public void createLink(TN p1, TN p2) { 64 | createLink(existingLinks, p1.nodeId, p2.nodeId); 65 | } 66 | 67 | public void removeLink(TN p1, TN p2) { 68 | removeLink(existingLinks, p1.nodeId, p2.nodeId); 69 | } 70 | 71 | private void createLink(Set existingLinks, int pp1, int pp2) { 72 | if (pp1 == pp2) { 73 | return; 74 | } 75 | long l1 = Math.min(pp1, pp2); 76 | long l2 = Math.max(pp1, pp2); 77 | long link = (l1 << 32) + l2; 78 | if (!existingLinks.add(link)) { 79 | return; 80 | } 81 | 82 | TN p1 = allNodes.get(pp1); 83 | TN p2 = allNodes.get(pp2); 84 | 85 | if (p1 == null || p2 == null) { 86 | throw new IllegalStateException( 87 | "should not be null: p1=" + p1 + ", p2=" + p2 + ", pp1=" + pp1 + ", pp2=" + pp2); 88 | } 89 | 90 | p1.peers.add(p2); 91 | p2.peers.add(p1); 92 | } 93 | 94 | private void removeLink(Set existingLinks, int pp1, int pp2) { 95 | if (pp1 == pp2) { 96 | return; 97 | } 98 | long l1 = Math.min(pp1, pp2); 99 | long l2 = Math.max(pp1, pp2); 100 | long link = (l1 << 32) + l2; 101 | if (!existingLinks.remove(link)) { 102 | throw new IllegalStateException("link between " + pp1 + " and " + pp2 + " does not exist"); 103 | } 104 | TN p1 = allNodes.get(pp1); 105 | TN p2 = allNodes.get(pp2); 106 | if (p1 == null || p2 == null) { 107 | throw new IllegalStateException( 108 | "should not be null: p1=" + p1 + ", p2=" + p2 + ", pp1=" + pp1 + ", pp2=" + pp2); 109 | } 110 | 111 | p1.peers.remove(p2); 112 | p2.peers.remove(p1); 113 | } 114 | 115 | public int avgPeers() { 116 | if (allNodes.isEmpty()) { 117 | return 0; 118 | } 119 | 120 | long tot = 0; 121 | for (TN n : allNodes) { 122 | tot += n.peers.size(); 123 | } 124 | return (int) (tot / allNodes.size()); 125 | } 126 | 127 | public void sendPeers(FloodMessage msg, TN from) { 128 | msg.addToReceived(from); 129 | List dest = new ArrayList<>(from.peers); 130 | Collections.shuffle(dest, rd); 131 | send(msg, time + 1 + msg.localDelay, from, dest, msg.delayBetweenPeers); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/P2PNode.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 | import java.util.*; 5 | import net.consensys.wittgenstein.core.json.ListNodeConverter; 6 | import net.consensys.wittgenstein.core.messages.FloodMessage; 7 | 8 | public class P2PNode extends Node { 9 | 10 | @JsonSerialize(converter = ListNodeConverter.class) 11 | public final List peers = new ArrayList<>(); 12 | 13 | protected Map> received = new HashMap<>(); 14 | 15 | public Set getMsgReceived(long id) { 16 | return received.computeIfAbsent(id, k -> new HashSet<>()); 17 | } 18 | 19 | public P2PNode(Random rd, NodeBuilder nb) { 20 | this(rd, nb, false); 21 | } 22 | 23 | public P2PNode(Random rd, NodeBuilder nb, boolean byzantine) { 24 | super(rd, nb, byzantine); 25 | } 26 | 27 | public void onFlood(TN from, FloodMessage floodMessage) {} 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/Protocol.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | /** 4 | * The interface to implement when you implement a protocol. These interfaces are mainly used in the 5 | * context of a scenario, typically executing multiple runs to extract min/max/avg behavior on some 6 | * network conditions. A protocol must have a public constructor taking WParameters as the unique 7 | * parameter. 8 | */ 9 | public interface Protocol { 10 | 11 | /** @return the network used by this protocol */ 12 | Network network(); 13 | 14 | /** 15 | * Copy the protocol, in order to do another run. Init will be called again. This should recreate 16 | * the network. 17 | */ 18 | Protocol copy(); 19 | 20 | /** Initialize, ig. create all the nodes, byzantine or not & so on. */ 21 | void init(); 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/RegistryNetworkLatencies.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public class RegistryNetworkLatencies { 8 | private Map registry = new HashMap<>(); 9 | 10 | public static RegistryNetworkLatencies singleton = new RegistryNetworkLatencies(); 11 | 12 | public enum Type { 13 | FIXED, 14 | UNIFORM 15 | } 16 | 17 | public static String name(Type t, int fixed) { 18 | switch (t) { 19 | case FIXED: 20 | return NetworkLatency.NetworkFixedLatency.class.getSimpleName() + "(" + fixed + ")"; 21 | case UNIFORM: 22 | return NetworkLatency.NetworkUniformLatency.class.getSimpleName() + "(" + fixed + ")"; 23 | } 24 | throw new IllegalStateException(); 25 | } 26 | 27 | public RegistryNetworkLatencies() { 28 | for (int f : new int[] {0, 100, 200, 500, 1000, 2000, 4000, 8000}) { 29 | registry.put(name(Type.FIXED, f), new NetworkLatency.NetworkFixedLatency(f)); 30 | registry.put(name(Type.UNIFORM, f), new NetworkLatency.NetworkUniformLatency(f)); 31 | } 32 | } 33 | 34 | public NetworkLatency getByName(String name) { 35 | if (name == null) { 36 | name = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 37 | } 38 | 39 | NetworkLatency nl = registry.get(name); 40 | if (nl != null) { 41 | return nl; 42 | } 43 | 44 | String ref = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getName(); 45 | String cut = 46 | ref.substring( 47 | 0, 48 | ref.length() 49 | - NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName().length()); 50 | String full = cut + name; 51 | try { 52 | Class cn = Class.forName(full); 53 | Constructor c = cn.getConstructor(); 54 | return (NetworkLatency) c.newInstance(); 55 | } catch (Exception e) { 56 | throw new IllegalArgumentException(e); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/RegistryNodeBuilders.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import net.consensys.wittgenstein.core.geoinfo.Geo; 6 | import net.consensys.wittgenstein.core.geoinfo.GeoAWS; 7 | import net.consensys.wittgenstein.core.geoinfo.GeoAllCities; 8 | import net.consensys.wittgenstein.tools.CSVLatencyReader; 9 | 10 | public class RegistryNodeBuilders { 11 | private Map registry = new HashMap<>(); 12 | 13 | public enum Location { 14 | AWS, 15 | CITIES, 16 | RANDOM 17 | } 18 | 19 | public static RegistryNodeBuilders singleton = new RegistryNodeBuilders(); 20 | 21 | public static String name(Location loc, boolean speedConstant, double tor) { 22 | String site = loc == Location.AWS ? "AWS" : loc == Location.RANDOM ? "RANDOM" : "CITIES"; 23 | String speed = speedConstant ? "CONSTANT" : "GAUSSIAN"; 24 | return (site + "_speed=" + speed + "_tor=" + (tor + "000").substring(0, 4)).toUpperCase(); 25 | } 26 | 27 | public static Double[] tor() { 28 | return new Double[] {0.0, 0.01, 0.10, 0.20, .33, .5, .6, .8, 1.0}; 29 | } 30 | 31 | public static Location[] locations() { 32 | return new Location[] {Location.AWS, Location.CITIES, Location.RANDOM}; 33 | } 34 | 35 | private RegistryNodeBuilders() { 36 | CSVLatencyReader lr = new CSVLatencyReader(); 37 | Geo geoAWS = new GeoAWS(); 38 | Geo geoAllCities = new GeoAllCities(); 39 | 40 | for (Location loc : locations()) { 41 | for (boolean speedConstant : new Boolean[] {true, false}) { 42 | for (double tor : tor()) { 43 | NodeBuilder nb; 44 | switch (loc) { 45 | case AWS: 46 | nb = 47 | new NodeBuilder.NodeBuilderWithCity( 48 | NetworkLatency.AwsRegionNetworkLatency.cities(), geoAWS); 49 | break; 50 | case CITIES: 51 | nb = new NodeBuilder.NodeBuilderWithCity(lr.cities(), geoAllCities); 52 | break; 53 | case RANDOM: 54 | nb = new NodeBuilder.NodeBuilderWithRandomPosition(); 55 | break; 56 | default: 57 | throw new IllegalStateException(); 58 | } 59 | if (!speedConstant) { 60 | nb.aspects.add(new Node.SpeedRatioAspect(new Node.UniformSpeed())); 61 | } 62 | if (tor > 0.001) { 63 | nb.aspects.add(new Node.ExtraLatencyAspect(tor)); 64 | } 65 | registry.put(name(loc, speedConstant, tor), nb); 66 | } 67 | } 68 | } 69 | } 70 | 71 | public NodeBuilder getByName(String name) { 72 | if (name == null || name.trim().isEmpty()) { 73 | name = name(Location.RANDOM, true, 0); 74 | } 75 | 76 | NodeBuilder c = registry.get(name); 77 | if (c == null) { 78 | throw new IllegalArgumentException(name + " not in the registry (" + registry + ")"); 79 | } 80 | return c.copy(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/RunMultipleTimes.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.function.Predicate; 8 | import net.consensys.wittgenstein.core.utils.StatsHelper; 9 | 10 | /** 11 | * Allows to run a scenario multiple time. The returned Stat object is an average of all the values. 12 | */ 13 | public class RunMultipleTimes { 14 | private final TP p; 15 | private final int runCount; 16 | private final int maxTime; 17 | private final List statsGetters; 18 | private final Predicate finalCheck; 19 | 20 | /** 21 | * @param p - the protocol to execute 22 | * @param runCount - the number of type we should run this protocol 23 | * @param maxTime - the maximum execution time for a single execution, 0 for infinite time 24 | * @param statsGetters - the stats to gather at the end of an execution 25 | * @param finalCheck - a check to perform at the end of each execution, to validate the execution 26 | * ran ok. Can be null. 27 | */ 28 | public RunMultipleTimes( 29 | TP p, 30 | int runCount, 31 | int maxTime, 32 | List statsGetters, 33 | Predicate finalCheck) { 34 | this.p = p; 35 | this.runCount = runCount; 36 | this.statsGetters = statsGetters; 37 | this.maxTime = maxTime; 38 | this.finalCheck = finalCheck; 39 | } 40 | 41 | public List run(Predicate contIf) { 42 | Map> allStats = new HashMap<>(); 43 | 44 | for (int i = 0; i < runCount; i++) { 45 | @SuppressWarnings("unchecked") 46 | TP c = (TP) p.copy(); 47 | c.network().rd.setSeed(i); 48 | c.init(); 49 | boolean didSomething; 50 | do { 51 | try { 52 | didSomething = c.network().runMs(10); 53 | } catch (Throwable t) { 54 | throw new IllegalStateException( 55 | "Failed execution of " 56 | + c 57 | + " for random seed of " 58 | + i 59 | + ", time=" 60 | + c.network().time, 61 | t); 62 | } 63 | } while ((maxTime == 0 || c.network().time < maxTime) 64 | && (!didSomething || (contIf != null && contIf.test(c)))); 65 | 66 | if (finalCheck != null && !finalCheck.test(c)) { 67 | throw new IllegalStateException("Failed execution of " + c + " for random seed of " + i); 68 | } 69 | 70 | for (StatsHelper.StatsGetter sg : statsGetters) { 71 | List res = allStats.computeIfAbsent(sg, (k) -> new ArrayList<>()); 72 | StatsHelper.Stat s = sg.get(c.network().liveNodes()); 73 | res.add(s); 74 | } 75 | } 76 | 77 | List res = new ArrayList<>(); 78 | for (StatsHelper.StatsGetter sg : statsGetters) { 79 | List stats = allStats.get(sg); 80 | StatsHelper.Stat s = StatsHelper.avg(stats); 81 | res.add(s); 82 | } 83 | 84 | return res; 85 | } 86 | 87 | /** A simple continuation criteria where we check for the done status. */ 88 | public static Predicate contUntilDone() { 89 | return p -> { 90 | for (Node n : p.network().liveNodes()) { 91 | if (n.doneAt == 0) { 92 | return true; 93 | } 94 | } 95 | return false; 96 | }; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/WParameters.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import net.consensys.wittgenstein.core.utils.Strings; 5 | 6 | /** 7 | * A value object containing all the parameters for a protocol. This will be serialized to/from a 8 | * json object, allowing to run a protocol from a distant system using http/json calls. 9 | */ 10 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 11 | public class WParameters { 12 | @Override 13 | public String toString() { 14 | return Strings.toString(this); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/geoinfo/CityInfo.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.geoinfo; 2 | 3 | public class CityInfo { 4 | public final int mercX; 5 | public final int mercY; 6 | public final float cumulativeProbability; 7 | 8 | public CityInfo(int mercX, int mercY, float cumulativeProbability) { 9 | this.mercX = mercX; 10 | this.mercY = mercY; 11 | this.cumulativeProbability = cumulativeProbability; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/geoinfo/Geo.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.geoinfo; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public abstract class Geo { 7 | 8 | public abstract Map citiesPosition(); 9 | 10 | protected Map cityInfoMap(Map cities, int totalPopulation) { 11 | float cumulativeProbability = 0f; 12 | Map citiesInfo = new HashMap<>(); 13 | for (Map.Entry e : cities.entrySet()) { 14 | cumulativeProbability = cumulativeProbability + e.getValue()[2] * 1.f / totalPopulation; 15 | CityInfo cityInfo = new CityInfo(e.getValue()[0], e.getValue()[1], cumulativeProbability); 16 | citiesInfo.put(e.getKey(), cityInfo); 17 | } 18 | return citiesInfo; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/geoinfo/GeoAWS.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.geoinfo; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class GeoAWS extends Geo { 7 | private Map cities = new HashMap<>(); 8 | 9 | public GeoAWS() { 10 | // We have only the positions for the cities 11 | // in the AWS network latency object 12 | cities.put("Oregon", new int[] {271, 261, 1}); 13 | cities.put("Virginia", new int[] {513, 316, 1}); 14 | cities.put("Mumbai", new int[] {1344, 426, 1}); 15 | cities.put("Seoul", new int[] {1641, 312, 1}); 16 | cities.put("Singapore", new int[] {1507, 532, 1}); 17 | cities.put("Sydney", new int[] {1773, 777, 1}); 18 | cities.put("Tokyo", new int[] {1708, 316, 1}); 19 | cities.put("Canada central", new int[] {422, 256, 1}); 20 | cities.put("Frankfurt", new int[] {985, 226, 1}); 21 | cities.put("Ireland", new int[] {891, 200, 1}); 22 | cities.put("London", new int[] {937, 205, 1}); 23 | } 24 | 25 | public Map citiesPosition() { 26 | return cityInfoMap(cities, cities.size()); 27 | } 28 | 29 | public static void main(String[] args) { 30 | Geo g = new GeoAWS(); 31 | CityInfo c = g.citiesPosition().get("tokyo"); 32 | System.out.println("seoul" + " " + c.mercX + " " + c.mercY + " " + c.cumulativeProbability); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/geoinfo/GeoAllCities.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.geoinfo; 2 | 3 | import static net.consensys.wittgenstein.core.Node.MAX_X; 4 | import static net.consensys.wittgenstein.core.Node.MAX_Y; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | import java.io.Reader; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import org.apache.commons.csv.CSVFormat; 12 | import org.apache.commons.csv.CSVParser; 13 | import org.apache.commons.csv.CSVRecord; 14 | 15 | public class GeoAllCities extends Geo { 16 | private final Map citiesPosition; 17 | private final double mapWidth; 18 | private final double mapHeight; 19 | private static final String CITY_PATH = "cities.csv"; 20 | 21 | public GeoAllCities() { 22 | this.mapWidth = MAX_X; 23 | this.mapHeight = MAX_Y; 24 | this.citiesPosition = readCityInfo(CITY_PATH); 25 | } 26 | 27 | public Map citiesPosition() { 28 | return new HashMap<>(citiesPosition); 29 | } 30 | 31 | private Map readCityInfo(String path) { 32 | Map cities = new HashMap<>(); 33 | 34 | int totalPopulation = 0; 35 | try (Reader reader = 36 | new InputStreamReader(getClass().getClassLoader().getResourceAsStream(path)); 37 | CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())) { 38 | 39 | for (CSVRecord csvRecord : csvParser) { 40 | String cityName = csvRecord.get(0).replace(' ', '+'); 41 | float latitude = Float.valueOf(csvRecord.get(1)); 42 | float longitude = Float.valueOf(csvRecord.get(2)); 43 | int mercX = convertToMercatorX(longitude); 44 | int mercY = convertToMercatorY(latitude); 45 | int population = Integer.valueOf(csvRecord.get(3)); 46 | 47 | population += 200000; 48 | 49 | totalPopulation += population; 50 | cities.put(cityName, new int[] {mercX, mercY, population}); 51 | } 52 | } catch (IOException e) { 53 | throw new IllegalStateException(e); 54 | } 55 | return cityInfoMap(cities, totalPopulation); 56 | } 57 | 58 | private int convertToMercatorX(double longitude) { 59 | int posX = (int) ((longitude + 180) * (mapWidth / 360)); 60 | if (posX < mapWidth / 2) { 61 | posX = posX - 45; 62 | } else { 63 | posX = posX - 70; 64 | } 65 | return posX; 66 | } 67 | 68 | private int convertToMercatorY(float latitude) { 69 | double latRad = latitude * Math.PI / 180; 70 | double mercN = Math.log(Math.tan((Math.PI / 4) + (latRad / 2))); 71 | int posY = (int) Math.round((mapHeight / 2) - (latitude * mapHeight / 180)); 72 | if (posY < 0.2 * mapHeight) { 73 | posY = posY - 35; 74 | } 75 | return posY; 76 | } 77 | 78 | // main method for testing 79 | public static void main(String[] args) { 80 | Geo geoInfo = new GeoAllCities(); 81 | CityInfo p = geoInfo.citiesPosition().get("New+York"); 82 | 83 | System.out.println("ny " + p.mercX + ", " + p.mercY + " " + p.cumulativeProbability); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/json/ExternalConverter.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.json; 2 | 3 | import com.fasterxml.jackson.databind.util.StdConverter; 4 | import net.consensys.wittgenstein.core.External; 5 | 6 | public class ExternalConverter extends StdConverter { 7 | @Override 8 | public String convert(External value) { 9 | return value == null ? "false" : value.toString(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/json/ListNodeConverter.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.json; 2 | 3 | import com.fasterxml.jackson.databind.util.StdConverter; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import net.consensys.wittgenstein.core.Node; 8 | 9 | public class ListNodeConverter extends StdConverter, List> { 10 | @Override 11 | public List convert(List ns) { 12 | List res = new ArrayList<>(); 13 | for (Node n : ns) { 14 | res.add(n.nodeId); 15 | } 16 | Collections.sort(res); 17 | return res; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/json/NodeConverter.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.json; 2 | 3 | import com.fasterxml.jackson.databind.util.StdConverter; 4 | import net.consensys.wittgenstein.core.Node; 5 | 6 | public class NodeConverter extends StdConverter { 7 | @Override 8 | public Integer convert(Node ns) { 9 | return ns.nodeId; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/messages/ConditionalTask.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.messages; 2 | 3 | import net.consensys.wittgenstein.core.Network; 4 | import net.consensys.wittgenstein.core.Node; 5 | 6 | public final class ConditionalTask extends Task { 7 | /** Starts if this condition is met. */ 8 | public final Network.Condition startIf; 9 | 10 | /** Will start again when the task is finished if this condition is met. */ 11 | public final Network.Condition repeatIf; 12 | 13 | /** Time before next start. */ 14 | public final int duration; 15 | 16 | /** Will start after this time. */ 17 | public int minStartTime; 18 | 19 | /** The node on which the task is executed. The task won't be executed if the node is down */ 20 | public final TN from; 21 | 22 | public ConditionalTask( 23 | Network.Condition startIf, 24 | Network.Condition repeatIf, 25 | Runnable r, 26 | int minStartTime, 27 | TN from, 28 | int duration) { 29 | super(r); 30 | this.startIf = startIf; 31 | this.repeatIf = repeatIf; 32 | this.from = from; 33 | this.duration = duration; 34 | this.minStartTime = minStartTime; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/messages/FloodMessage.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.messages; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | import net.consensys.wittgenstein.core.Network; 7 | import net.consensys.wittgenstein.core.P2PNode; 8 | 9 | /** 10 | * A P2P node supports flood by default. In P2P protocols: - you can wait before resending the 11 | * message to your peers, for example because you're validating the message. - you can send the 12 | * message to all your peers immediately, or one after another with a delay between each send. Here, 13 | * we embed this logic in the message; allowing each message to have a different strategy. 14 | */ 15 | public class FloodMessage> extends Message { 16 | protected final int size; 17 | /** 18 | * The delay before we send this message to the other nodes, for example if we need to validate 19 | * the message before diffusing it. 20 | */ 21 | public final int localDelay; 22 | /** 23 | * It's possible to send the message immediately to all peers, but as well to wait between peers. 24 | */ 25 | public final int delayBetweenPeers; 26 | 27 | public long msgId() { 28 | return -1; 29 | } 30 | 31 | // for json 32 | @SuppressWarnings("unused") 33 | public FloodMessage() { 34 | this(0, 0, 0); 35 | } 36 | 37 | public FloodMessage(int size, int localDelay, int delayBetweenPeers) { 38 | this.size = size; 39 | this.localDelay = localDelay; 40 | this.delayBetweenPeers = delayBetweenPeers; 41 | } 42 | 43 | public boolean addToReceived(TN to) { 44 | return to.getMsgReceived(msgId()).add(this); 45 | } 46 | 47 | @Override 48 | public void action(Network network, TN from, TN to) { 49 | if (addToReceived(to)) { 50 | to.onFlood(from, this); 51 | List dest = to.peers.stream().filter(n -> n != from).collect(Collectors.toList()); 52 | Collections.shuffle(dest, network.rd); 53 | network.send(this, network.time + 1 + localDelay, to, dest, delayBetweenPeers); 54 | } 55 | } 56 | 57 | @Override 58 | public int size() { 59 | return size; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/messages/Message.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.messages; 2 | 3 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 4 | import net.consensys.wittgenstein.core.Network; 5 | import net.consensys.wittgenstein.core.Node; 6 | import net.consensys.wittgenstein.core.utils.Strings; 7 | 8 | /** 9 | * The generic message that goes on a network. Triggers an 'action' on reception. 10 | * 11 | *

Object of this class must be immutable. Especially, Message is shared between the messages for 12 | * messages sent to multiple nodes. 13 | */ 14 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 15 | public abstract class Message { 16 | 17 | /** 18 | * Must be implemented by the protocol implementers to specify what happens when a node receive 19 | * this message. 20 | */ 21 | public abstract void action(Network network, TN from, TN to); 22 | 23 | /** 24 | * We track the total size of the messages exchanged. Subclasses should override this method if 25 | * they want to measure the network usage. 26 | */ 27 | public int size() { 28 | return 1; 29 | } 30 | 31 | /** 32 | * Default implementation, using reflection to print the fields value. Can be overridden by the 33 | * subclasses if they want to. 34 | */ 35 | @Override 36 | public String toString() { 37 | return Strings.toString(this); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/messages/PeriodicTask.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.messages; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import net.consensys.wittgenstein.core.Network; 6 | import net.consensys.wittgenstein.core.Node; 7 | import net.consensys.wittgenstein.core.json.NodeConverter; 8 | 9 | /** Some protocols want some tasks to be executed periodically */ 10 | @SuppressWarnings("WeakerAccess") 11 | public class PeriodicTask extends Task { 12 | public final int period; 13 | 14 | @JsonSerialize(converter = NodeConverter.class) 15 | public final TN sender; 16 | 17 | @JsonIgnore public final Network.Condition continuationCondition; 18 | 19 | public PeriodicTask(Runnable r, TN fromNode, int period, Network.Condition condition) { 20 | super(r); 21 | this.period = period; 22 | this.sender = fromNode; 23 | this.continuationCondition = condition; 24 | } 25 | 26 | // For json 27 | @SuppressWarnings("unused") 28 | public PeriodicTask() { 29 | super(null); 30 | period = -1; 31 | sender = null; 32 | continuationCondition = null; 33 | } 34 | 35 | public PeriodicTask(Runnable r, TN fromNode, int period) { 36 | this(r, fromNode, period, () -> true); 37 | } 38 | 39 | @Override 40 | public void action(Network network, TN from, TN to) { 41 | assert r != null; 42 | assert continuationCondition != null; 43 | r.run(); 44 | if (continuationCondition.check()) { 45 | network.sendArriveAt(this, network.time + period, sender, sender); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/messages/SendMessage.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.messages; 2 | 3 | import java.util.List; 4 | 5 | @SuppressWarnings("WeakerAccess") 6 | public class SendMessage { 7 | public final int from; 8 | public final List to; 9 | public final int sendTime; 10 | public final int delayBetweenSend; 11 | public final Message message; 12 | 13 | // For json 14 | public SendMessage() { 15 | this(0, null, 0, 0, null); 16 | } 17 | 18 | public SendMessage( 19 | int from, List to, int sendTime, int delayBetweenSend, Message message) { 20 | this.from = from; 21 | this.to = to; 22 | this.sendTime = sendTime; 23 | this.message = message; 24 | this.delayBetweenSend = delayBetweenSend; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/messages/StatusFloodMessage.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.messages; 2 | 3 | import java.util.Set; 4 | import net.consensys.wittgenstein.core.P2PNode; 5 | 6 | /** 7 | * Class to use when a same node will update the message, i.e. the new version will replace the old 8 | * ones. 9 | */ 10 | public class StatusFloodMessage> extends FloodMessage { 11 | /** 12 | * The message id. It's the same for all versions. The message id must be globally unique, i.e. if 13 | * multiple nodes are sending the same type of message they need to have two different msg id. 14 | */ 15 | final int msgId; 16 | /** The version number. */ 17 | final int seq; 18 | 19 | public StatusFloodMessage(int msgId, int seq, int size, int localDelay, int delayBetweenPeers) { 20 | super(size, localDelay, delayBetweenPeers); 21 | if (msgId < 0) { 22 | throw new IllegalStateException("id less than zero are reserved, msgId=" + msgId); 23 | } 24 | this.msgId = msgId; 25 | this.seq = seq; 26 | } 27 | 28 | @Override 29 | public long msgId() { 30 | return msgId; 31 | } 32 | 33 | /** We're adding this message to the node's received set only if the seq number is greater. */ 34 | @Override 35 | public boolean addToReceived(TN to) { 36 | Set previousSet = to.getMsgReceived(msgId); 37 | Object previous = previousSet.isEmpty() ? null : previousSet.iterator().next(); 38 | StatusFloodMessage psf = (StatusFloodMessage) previous; 39 | if (psf != null && psf.seq >= seq) { 40 | return false; 41 | } 42 | previousSet.clear(); // By definition we want only one element in the set 43 | to.getMsgReceived(msgId).add(this); 44 | return true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/messages/Task.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.messages; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import net.consensys.wittgenstein.core.Network; 5 | import net.consensys.wittgenstein.core.Node; 6 | 7 | /** Some protocols want some tasks to be executed at a given time */ 8 | public class Task extends Message { 9 | @JsonIgnore public final Runnable r; 10 | 11 | public Task(Runnable r) { 12 | assert r != null; 13 | this.r = r; 14 | } 15 | 16 | // For json 17 | public Task() { 18 | r = null; 19 | } 20 | 21 | @Override 22 | public int size() { 23 | return 0; 24 | } 25 | 26 | @Override 27 | public void action(Network network, TN from, TN to) { 28 | assert r != null; 29 | r.run(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/utils/BitSetUtils.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.utils; 2 | 3 | import java.util.BitSet; 4 | 5 | public class BitSetUtils { 6 | 7 | /** @return true if the 'small' bitset is included in the big one. */ 8 | public static boolean include(BitSet big, BitSet small) { 9 | BitSet b = (BitSet) small.clone(); 10 | b.or(big); 11 | 12 | return b.equals(big); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/utils/GeneralizedParetoDistribution.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.utils; 2 | 3 | public class GeneralizedParetoDistribution { 4 | /** shape ξ */ 5 | private final double shape; 6 | 7 | /** location μ */ 8 | private final double location; 9 | 10 | /** scale σ */ 11 | private final double scale; 12 | 13 | private static final double ONE = 0.999999; 14 | private static final double ZERO = 0.000001; 15 | 16 | public GeneralizedParetoDistribution(double shape, double location, double scale) { 17 | if (scale <= 0.0) { 18 | throw new IllegalArgumentException("scale=" + scale); 19 | } 20 | 21 | this.shape = shape; 22 | this.location = location; 23 | this.scale = scale; 24 | } 25 | 26 | public double inverseF(double y) { 27 | if (y < 0.0 || y > 1.0) { 28 | throw new IllegalArgumentException("y=" + y); 29 | } 30 | 31 | if (y < ZERO) { 32 | return location; 33 | } 34 | 35 | if (y > ONE && shape >= 0) { 36 | return Double.POSITIVE_INFINITY; 37 | } 38 | 39 | if (y > ONE && shape < 0) { 40 | return location - scale / shape; 41 | } 42 | if (Math.abs(shape) < ZERO) { 43 | return location - scale * Math.log1p(-y); 44 | } 45 | return location + scale / shape * (-1 + Math.pow(1 - y, -shape)); 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "ξ=" + shape + ", μ=" + location + ", σ=" + scale; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/utils/Kademlia.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.utils; 2 | 3 | import java.util.Arrays; 4 | 5 | public class Kademlia { 6 | 7 | /** Calculates the XOR distance between two values. Taken from pantheon code. */ 8 | public static int distance(byte[] v1b, byte[] v2b) { 9 | assert (v1b.length == v2b.length); 10 | 11 | if (Arrays.equals(v1b, v2b)) { 12 | return 0; 13 | } 14 | 15 | int distance = v1b.length * 8; 16 | for (int i = 0; i < v1b.length; i++) { 17 | byte xor = (byte) (0xff & (v1b[i] ^ v2b[i])); 18 | if (xor == 0) { 19 | distance -= 8; 20 | } else { 21 | int p = 7; 22 | while (((xor >> p--) & 0x01) == 0) { 23 | distance--; 24 | } 25 | break; 26 | } 27 | } 28 | return distance; 29 | } 30 | 31 | /* 32 | * http://www.scs.stanford.edu/~dm/home/papers/kpos.pdf 33 | * 34 | * For each 0 =< i < 160, every node keeps a list of (IP address; UDP port; Node ID) 35 | * triples for nodes of distance between 2^i and 2^(i+1) from itself. We call these 36 | * lists k-buckets. 37 | * 38 | * When a Kademlia node receives any message (request or reply) from another node, it updates 39 | * the appropriate k-bucket for the sender’s nodeID. If the sending node already exists in the 40 | * recipient’s k-bucket, the recipient moves it to the tail of the list. If the node is not 41 | * already in the appropriate k-bucket and the bucket has fewer than k entries, then the 42 | * recipient just inserts the new sender at the tail of the list. If the appropriate 43 | * k-bucket is full, however, then the recipient pings the k-bucket’s least-recently 44 | * seen node to decide what to do. If the least-recently seen node fails to respond, it is 45 | * evicted from the k-bucket and the new sender inserted at the tail. Otherwise, if the 46 | * least-recently seen node responds, it is moved to the tail of the list, and the new sender’s 47 | * contact is discarded. 48 | * 49 | * The most important procedure a Kademlia participant must perform is to locate the 50 | * k closest nodes to some given node ID. We call this procedure a node lookup. 51 | * Kademlia employs a recursive algorithm for node lookups. The lookup initiator starts 52 | * by picking α nodes from its closest non-empty k-bucket (or, if that bucket has fewer 53 | * than α entries, it just takes the α closest nodes it knows of). The initiator then 54 | * sends parallel, asynchronous FIND NODE RPCs to the α nodes it has chosen. α is a 55 | * system-wide concurrency parameter, such as 3. 56 | * In the recursive step, the initiator resends the FIND NODE to nodes it has learned 57 | * about from previous RPCs. 58 | * The lookup terminates when the initiator has queried and gotten responses from the 59 | * k closest nodes it has seen. When α = 1 the lookup algorithm resembles Chord’s in 60 | * terms of message cost and the latency of detecting failed nodes. However, Kademlia 61 | * can route for lower latency because it has the flexibility of choosing 62 | * any one of k nodes to forward a request to. 63 | * 64 | * To join the network, a node u must have a contact to an already participating 65 | * node w. u inserts w into the appropriate k-bucket. u then performs a node lookup for 66 | * its own node ID. Finally, u refreshes all k-buckets further away than its closest 67 | * neighbor. During the refreshes, u both populates its own k-buckets and inserts itself 68 | * into other nodes’k-buckets as necessary. 69 | * 70 | * (https://github.com/ethereum/devp2p/blob/master/discv4.md) 71 | * Ethereum v1 uses a value of 16 for k, and 256 buckets instead of 160. 72 | */ 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/utils/MoreMath.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.utils; 2 | 3 | public class MoreMath { 4 | 5 | public static int log2(int n) { 6 | if (n <= 0) { 7 | throw new IllegalArgumentException("n=" + n); 8 | } 9 | return 31 - Integer.numberOfLeadingZeros(n); 10 | } 11 | 12 | /** @return n rounded to the next power of 2. n itself if it's already rounded. */ 13 | public static int roundPow2(int n) { 14 | int res = Integer.highestOneBit(n); 15 | if (res != n) { 16 | res = res << 1; 17 | } 18 | return res; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/utils/Reflects.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.utils; 2 | 3 | import java.lang.reflect.Array; 4 | import java.lang.reflect.Constructor; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | @SuppressWarnings({"unchecked", "WeakerAccess"}) 9 | public class Reflects { 10 | 11 | /** Create an object with dummy values if there is no default constructor. */ 12 | public static T newInstance(Class cls) { 13 | Constructor c = (Constructor) cls.getConstructors()[0]; 14 | 15 | List params = new ArrayList<>(); 16 | for (Class pType : c.getParameterTypes()) { 17 | params.add(getDefaultValue(pType)); 18 | } 19 | 20 | try { 21 | return c.newInstance(params.toArray()); 22 | } catch (Exception e) { 23 | throw new IllegalArgumentException(e); 24 | } 25 | } 26 | 27 | public static T getDefaultValue(Class clazz) { 28 | return (T) Array.get(Array.newInstance(clazz, 1), 0); 29 | } 30 | 31 | public static Class forName(String fullName) { 32 | if (fullName == null || fullName.isEmpty()) { 33 | throw new IllegalArgumentException("class name is null or empty"); 34 | } 35 | try { 36 | return Class.forName(fullName); 37 | } catch (ClassNotFoundException e) { 38 | throw new IllegalArgumentException(e); 39 | } 40 | } 41 | 42 | public static T newInstance(Constructor constructor, Object... args) { 43 | try { 44 | return constructor.newInstance(args); 45 | } catch (Exception e) { 46 | throw new IllegalArgumentException( 47 | "Constructor not valid " + constructor + " error message" + e); 48 | } 49 | } 50 | 51 | /* 52 | public static List wittgensteinPackages(){ 53 | List res = new ArrayList<>(); 54 | Reflects.class.getClassLoader().getDefinedPackages() 55 | }*/ 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/core/utils/Strings.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core.utils; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | public class Strings { 6 | 7 | public static String toString(Object o) { 8 | StringBuilder sb = new StringBuilder(); 9 | 10 | for (Field f : o.getClass().getDeclaredFields()) { 11 | try { 12 | f.setAccessible(true); 13 | String v = "" + f.get(o); 14 | if (sb.length() != 0) { 15 | sb.append(", "); 16 | } 17 | sb.append(f.getName()).append("=").append(v); 18 | } catch (IllegalAccessException ignore) { 19 | } 20 | } 21 | 22 | return o.getClass().getSimpleName() + "{" + sb.toString() + "}"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/net/consensys/wittgenstein/tools/CSVFormatter.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.tools; 2 | 3 | import java.util.*; 4 | import java.util.stream.Collectors; 5 | 6 | public class CSVFormatter { 7 | private static class EmptyObject { 8 | @Override 9 | public String toString() { 10 | return ""; 11 | } 12 | } 13 | 14 | private final List fields; 15 | private final Map> values; 16 | private int nbRows; 17 | 18 | public CSVFormatter(List fields) { 19 | this.fields = fields; 20 | this.values = new HashMap<>(); 21 | this.nbRows = 0; 22 | } 23 | 24 | /** 25 | * add each values to its corresponding value to the CSV. It corresponds to a new row in the CSV 26 | * output. Empty fields will be filled out by empty value when printing the CSV. 27 | */ 28 | public void add(Map vals) { 29 | for (String field : fields) { 30 | List l = this.values.getOrDefault(field, new ArrayList<>()); 31 | Object value = vals.get(field); 32 | l.add(Objects.requireNonNullElseGet(value, EmptyObject::new)); 33 | this.values.put(field, l); 34 | } 35 | this.nbRows++; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | String headers = Headers() + "\n"; 41 | StringBuilder rows = new StringBuilder(); 42 | for (int i = 0; i < nbRows; i++) { 43 | List rowValues = new ArrayList<>(); 44 | for (String field : fields) { 45 | Object value = values.get(field).get(i); 46 | rowValues.add(value); 47 | } 48 | String line = rowValues.stream().map(Object::toString).collect(Collectors.joining(",")); 49 | rows.append(line).append('\n'); 50 | } 51 | return headers + rows; 52 | } 53 | 54 | private String Headers() { 55 | return String.join(",", fields); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/resources/Data/Adelaide/Adelaide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Adelaide/Adelaide.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Albany/Albany.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Albany/Albany.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Alblasserdam/Alblasserdam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Alblasserdam/Alblasserdam.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Albuquerque/Albuquerque.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Albuquerque/Albuquerque.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Algiers/Algiers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Algiers/Algiers.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Amsterdam/Amsterdam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Amsterdam/Amsterdam.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Anchorage/Anchorage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Anchorage/Anchorage.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Ankara/Ankara.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Ankara/Ankara.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Antwerp/Antwerp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Antwerp/Antwerp.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Arezzo/Arezzo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Arezzo/Arezzo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Asheville/Asheville.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Asheville/Asheville.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Athens/Athens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Athens/Athens.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Atlanta/Atlanta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Atlanta/Atlanta.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Auckland/Auckland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Auckland/Auckland.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Austin/Austin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Austin/Austin.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Baltimore/Baltimore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Baltimore/Baltimore.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bangalore/Bangalore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bangalore/Bangalore.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bangkok/Bangkok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bangkok/Bangkok.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Barcelona/Barcelona.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Barcelona/Barcelona.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Basel/Basel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Basel/Basel.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Belfast/Belfast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Belfast/Belfast.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bergen/Bergen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bergen/Bergen.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Berkeley+Springs/Berkeley+Springs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Berkeley+Springs/Berkeley+Springs.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bern/Bern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bern/Bern.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bogota/Bogota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bogota/Bogota.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Boston/Boston.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Boston/Boston.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Brasilia/Brasilia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Brasilia/Brasilia.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bratislava/Bratislava.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bratislava/Bratislava.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Brisbane/Brisbane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Brisbane/Brisbane.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bristol/Bristol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bristol/Bristol.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Brno/Brno.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Brno/Brno.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bruges/Bruges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bruges/Bruges.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Brunswick/Brunswick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Brunswick/Brunswick.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Brussels/Brussels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Brussels/Brussels.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bucharest/Bucharest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bucharest/Bucharest.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Budapest/Budapest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Budapest/Budapest.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Buenos+Aires/Buenos+Aires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Buenos+Aires/Buenos+Aires.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Buffalo/Buffalo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Buffalo/Buffalo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Bursa/Bursa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Bursa/Bursa.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Cairo/Cairo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Cairo/Cairo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Calgary/Calgary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Calgary/Calgary.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Canberra/Canberra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Canberra/Canberra.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Cape+Town/Cape+Town.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Cape+Town/Cape+Town.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Caracas/Caracas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Caracas/Caracas.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Cardiff/Cardiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Cardiff/Cardiff.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Charlotte/Charlotte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Charlotte/Charlotte.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Cheltenham/Cheltenham.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Cheltenham/Cheltenham.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Chennai/Chennai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Chennai/Chennai.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Chicago/Chicago.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Chicago/Chicago.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Chisinau/Chisinau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Chisinau/Chisinau.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Christchurch/Christchurch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Christchurch/Christchurch.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Cincinnati/Cincinnati.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Cincinnati/Cincinnati.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Cleveland/Cleveland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Cleveland/Cleveland.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Colorado+Springs/Colorado+Springs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Colorado+Springs/Colorado+Springs.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Columbus/Columbus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Columbus/Columbus.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Copenhagen/Copenhagen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Copenhagen/Copenhagen.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Coventry/Coventry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Coventry/Coventry.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Cromwell/Cromwell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Cromwell/Cromwell.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dagupan/Dagupan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dagupan/Dagupan.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dallas/Dallas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dallas/Dallas.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dar+es+Salaam/Dar+es+Salaam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dar+es+Salaam/Dar+es+Salaam.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Denver/Denver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Denver/Denver.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Des+Moines/Des+Moines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Des+Moines/Des+Moines.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Detroit/Detroit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Detroit/Detroit.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dhaka/Dhaka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dhaka/Dhaka.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dronten/Dronten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dronten/Dronten.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dubai/Dubai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dubai/Dubai.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dublin/Dublin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dublin/Dublin.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Dusseldorf/Dusseldorf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Dusseldorf/Dusseldorf.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Edinburgh/Edinburgh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Edinburgh/Edinburgh.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Eindhoven/Eindhoven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Eindhoven/Eindhoven.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Falkenstein/Falkenstein.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Falkenstein/Falkenstein.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Fez/Fez.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Fez/Fez.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Frankfurt/Frankfurt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Frankfurt/Frankfurt.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Fremont/Fremont.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Fremont/Fremont.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Frosinone/Frosinone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Frosinone/Frosinone.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Gdansk/Gdansk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Gdansk/Gdansk.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Geneva/Geneva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Geneva/Geneva.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Gothenburg/Gothenburg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Gothenburg/Gothenburg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Green+Bay/Green+Bay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Green+Bay/Green+Bay.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Groningen/Groningen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Groningen/Groningen.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Halifax/Halifax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Halifax/Halifax.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Hamburg/Hamburg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Hamburg/Hamburg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Hangzhou/Hangzhou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Hangzhou/Hangzhou.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Hanoi/Hanoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Hanoi/Hanoi.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Helsinki/Helsinki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Helsinki/Helsinki.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Heredia/Heredia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Heredia/Heredia.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Ho+Chi+Minh+City/Ho+Chi+Minh+City.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Ho+Chi+Minh+City/Ho+Chi+Minh+City.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Hong+Kong/Hong+Kong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Hong+Kong/Hong+Kong.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Honolulu/Honolulu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Honolulu/Honolulu.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Houston/Houston.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Houston/Houston.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Hyderabad/Hyderabad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Hyderabad/Hyderabad.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Indianapolis/Indianapolis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Indianapolis/Indianapolis.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Indore/Indore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Indore/Indore.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Istanbul/Istanbul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Istanbul/Istanbul.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Izmir/Izmir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Izmir/Izmir.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Jackson/Jackson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Jackson/Jackson.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Jacksonville/Jacksonville.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Jacksonville/Jacksonville.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Jakarta/Jakarta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Jakarta/Jakarta.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Jerusalem/Jerusalem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Jerusalem/Jerusalem.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Joao+Pessoa/Joao+Pessoa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Joao+Pessoa/Joao+Pessoa.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Johannesburg/Johannesburg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Johannesburg/Johannesburg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Kampala/Kampala.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Kampala/Kampala.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Kansas+City/Kansas+City.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Kansas+City/Kansas+City.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Karaganda/Karaganda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Karaganda/Karaganda.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Kiev/Kiev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Kiev/Kiev.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Knoxville/Knoxville.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Knoxville/Knoxville.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Koto/Koto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Koto/Koto.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Ktis/Ktis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Ktis/Ktis.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/La+Ceiba/La+Ceiba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/La+Ceiba/La+Ceiba.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lagos/Lagos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lagos/Lagos.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lahore/Lahore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lahore/Lahore.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Las+Vegas/Las+Vegas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Las+Vegas/Las+Vegas.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lausanne/Lausanne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lausanne/Lausanne.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Liege/Liege.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Liege/Liege.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lima/Lima.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lima/Lima.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Limassol/Limassol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Limassol/Limassol.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lincoln/Lincoln.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lincoln/Lincoln.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lisbon/Lisbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lisbon/Lisbon.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Ljubljana/Ljubljana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Ljubljana/Ljubljana.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/London/London.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/London/London.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Los+Angeles/Los+Angeles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Los+Angeles/Los+Angeles.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Louisville/Louisville.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Louisville/Louisville.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lugano/Lugano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lugano/Lugano.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Luxembourg/Luxembourg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Luxembourg/Luxembourg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Lyon/Lyon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Lyon/Lyon.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Madrid/Madrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Madrid/Madrid.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Maidstone/Maidstone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Maidstone/Maidstone.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Malaysia/Malaysia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Malaysia/Malaysia.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Manchester/Manchester.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Manchester/Manchester.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Manhattan/Manhattan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Manhattan/Manhattan.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Manila/Manila.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Manila/Manila.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Marseille/Marseille.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Marseille/Marseille.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Medellin/Medellin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Medellin/Medellin.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Melbourne/Melbourne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Melbourne/Melbourne.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Memphis/Memphis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Memphis/Memphis.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Mexico/Mexico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Mexico/Mexico.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Miami/Miami.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Miami/Miami.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Milan/Milan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Milan/Milan.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Milwaukee/Milwaukee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Milwaukee/Milwaukee.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Minneapolis/Minneapolis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Minneapolis/Minneapolis.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Missoula/Missoula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Missoula/Missoula.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Montevideo/Montevideo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Montevideo/Montevideo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Monticello/Monticello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Monticello/Monticello.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Montreal/Montreal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Montreal/Montreal.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Moscow/Moscow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Moscow/Moscow.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Munich/Munich.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Munich/Munich.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Nairobi/Nairobi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Nairobi/Nairobi.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/New+Delhi/New+Delhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/New+Delhi/New+Delhi.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/New+Orleans/New+Orleans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/New+Orleans/New+Orleans.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/New+York/New+York.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/New+York/New+York.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Newcastle/Newcastle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Newcastle/Newcastle.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Nis/Nis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Nis/Nis.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Novosibirsk/Novosibirsk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Novosibirsk/Novosibirsk.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Nuremberg/Nuremberg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Nuremberg/Nuremberg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Oklahoma+City/Oklahoma+City.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Oklahoma+City/Oklahoma+City.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Orlando/Orlando.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Orlando/Orlando.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Osaka/Osaka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Osaka/Osaka.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Oslo/Oslo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Oslo/Oslo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Ottawa/Ottawa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Ottawa/Ottawa.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Palermo/Palermo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Palermo/Palermo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Panama/Panama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Panama/Panama.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Paramaribo/Paramaribo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Paramaribo/Paramaribo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Paris/Paris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Paris/Paris.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Perth/Perth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Perth/Perth.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Philadelphia/Philadelphia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Philadelphia/Philadelphia.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Phnom+Penh/Phnom+Penh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Phnom+Penh/Phnom+Penh.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Phoenix/Phoenix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Phoenix/Phoenix.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Piscataway/Piscataway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Piscataway/Piscataway.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Pittsburgh/Pittsburgh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Pittsburgh/Pittsburgh.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Portland/Portland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Portland/Portland.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Prague/Prague.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Prague/Prague.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Pune/Pune.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Pune/Pune.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Quebec+City/Quebec+City.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Quebec+City/Quebec+City.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Quito/Quito.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Quito/Quito.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Raleigh/Raleigh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Raleigh/Raleigh.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Redding/Redding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Redding/Redding.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Reykjavik/Reykjavik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Reykjavik/Reykjavik.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Richmond/Richmond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Richmond/Richmond.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Riga/Riga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Riga/Riga.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Riyadh/Riyadh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Riyadh/Riyadh.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Rome/Rome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Rome/Rome.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Roseburg/Roseburg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Roseburg/Roseburg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Rotterdam/Rotterdam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Rotterdam/Rotterdam.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Roubaix/Roubaix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Roubaix/Roubaix.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Sacramento/Sacramento.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Sacramento/Sacramento.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Salem/Salem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Salem/Salem.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Salt+Lake+City/Salt+Lake+City.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Salt+Lake+City/Salt+Lake+City.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/San+Antonio/San+Antonio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/San+Antonio/San+Antonio.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/San+Diego/San+Diego.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/San+Diego/San+Diego.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/San+Francisco/San+Francisco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/San+Francisco/San+Francisco.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/San+Jose/San+Jose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/San+Jose/San+Jose.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/San+Juan/San+Juan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/San+Juan/San+Juan.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Santiago/Santiago.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Santiago/Santiago.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Sao+Paulo/Sao+Paulo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Sao+Paulo/Sao+Paulo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Sapporo/Sapporo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Sapporo/Sapporo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Saskatoon/Saskatoon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Saskatoon/Saskatoon.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Savannah/Savannah.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Savannah/Savannah.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Scranton/Scranton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Scranton/Scranton.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Seattle/Seattle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Seattle/Seattle.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Secaucus/Secaucus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Secaucus/Secaucus.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Seoul/Seoul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Seoul/Seoul.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Shanghai/Shanghai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Shanghai/Shanghai.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Shenzhen/Shenzhen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Shenzhen/Shenzhen.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Singapore/Singapore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Singapore/Singapore.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Sofia/Sofia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Sofia/Sofia.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/South+Bend/South+Bend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/South+Bend/South+Bend.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/St+Louis/St+Louis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/St+Louis/St+Louis.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/St+Petersburg/St+Petersburg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/St+Petersburg/St+Petersburg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Stamford/Stamford.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Stamford/Stamford.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Stockholm/Stockholm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Stockholm/Stockholm.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Strasbourg/Strasbourg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Strasbourg/Strasbourg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Sydney/Sydney.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Sydney/Sydney.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Syracuse/Syracuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Syracuse/Syracuse.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Taipei/Taipei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Taipei/Taipei.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tallinn/Tallinn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tallinn/Tallinn.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tampa/Tampa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tampa/Tampa.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tbilisi/Tbilisi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tbilisi/Tbilisi.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tel+Aviv/Tel+Aviv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tel+Aviv/Tel+Aviv.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tempe/Tempe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tempe/Tempe.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/The+Hague/The+Hague.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/The+Hague/The+Hague.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Thessaloniki/Thessaloniki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Thessaloniki/Thessaloniki.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tirana/Tirana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tirana/Tirana.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tokyo/Tokyo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tokyo/Tokyo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Toledo/Toledo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Toledo/Toledo.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Toronto/Toronto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Toronto/Toronto.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Tunis/Tunis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Tunis/Tunis.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Valencia/Valencia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Valencia/Valencia.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Valletta/Valletta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Valletta/Valletta.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Vancouver/Vancouver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Vancouver/Vancouver.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Varna/Varna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Varna/Varna.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Venice/Venice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Venice/Venice.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Vienna/Vienna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Vienna/Vienna.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Vilnius/Vilnius.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Vilnius/Vilnius.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Vladivostok/Vladivostok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Vladivostok/Vladivostok.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Warsaw/Warsaw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Warsaw/Warsaw.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Washington/Washington.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Washington/Washington.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Wellington/Wellington.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Wellington/Wellington.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Westpoort/Westpoort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Westpoort/Westpoort.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Winnipeg/Winnipeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Winnipeg/Winnipeg.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Zhangjiakou/Zhangjiakou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Zhangjiakou/Zhangjiakou.png -------------------------------------------------------------------------------- /core/src/main/resources/Data/Zurich/Zurich.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/Data/Zurich/Zurich.png -------------------------------------------------------------------------------- /core/src/main/resources/world-map-2000px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/wittgenstein/fe0414c02f0ef576c9a0c7ccb29581be58862acf/core/src/main/resources/world-map-2000px.png -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/core/CityPopulationTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import net.consensys.wittgenstein.core.geoinfo.CityInfo; 7 | import net.consensys.wittgenstein.core.geoinfo.Geo; 8 | import net.consensys.wittgenstein.core.geoinfo.GeoAllCities; 9 | import net.consensys.wittgenstein.tools.CSVLatencyReader; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | class GeoTest extends Geo { 14 | private final Map citiesInfo; 15 | 16 | GeoTest(Map map, int totalPopulation) { 17 | citiesInfo = cityInfoMap(map, totalPopulation); 18 | } 19 | 20 | public Map citiesPosition() { 21 | return citiesInfo; 22 | } 23 | } 24 | 25 | public class CityPopulationTest { 26 | 27 | @Test 28 | public void testCumulativeProbability() { 29 | CSVLatencyReader lr = new CSVLatencyReader(); 30 | Assert.assertTrue(lr.cities().size() > 0); 31 | NodeBuilder.NodeBuilderWithCity nb = 32 | new NodeBuilder.NodeBuilderWithCity(lr.cities(), new GeoAllCities()); 33 | 34 | for (Map.Entry cityInfo : nb.getCitiesInfo().entrySet()) { 35 | Assert.assertTrue( 36 | "wrong cumulative probability for " 37 | + cityInfo.getKey() 38 | + ":" 39 | + cityInfo.getValue().cumulativeProbability, 40 | cityInfo.getValue().cumulativeProbability < 1.00001f); 41 | } 42 | } 43 | 44 | @Test 45 | public void tetDistribution() { 46 | Map cities = new HashMap<>(); 47 | cities.put("SmallCity0", new int[] {0, 0, 1}); 48 | cities.put("SmallCity1", new int[] {0, 0, 1}); 49 | cities.put("SmallCity2", new int[] {0, 0, 1}); 50 | cities.put("SmallCity3", new int[] {0, 0, 1}); 51 | cities.put("BigCity", new int[] {0, 0, 6}); 52 | 53 | int total = 10; 54 | Geo geo = new GeoTest(cities, total); 55 | 56 | NodeBuilder.NodeBuilderWithCity nb = 57 | new NodeBuilder.NodeBuilderWithCity(new ArrayList<>(cities.keySet()), geo); 58 | 59 | int bigCityCount = 0; 60 | for (int i = 0; i < total; i++) { 61 | String city = nb.getCityName(i); 62 | if (city.equals("BigCity")) { 63 | bigCityCount++; 64 | } 65 | } 66 | Assert.assertEquals( 67 | "Bigger cities should be selected more often than smaller", 6, bigCityCount); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/core/EnvelopeStorageTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.Random; 4 | import java.util.concurrent.atomic.AtomicBoolean; 5 | import java.util.concurrent.atomic.AtomicLong; 6 | import net.consensys.wittgenstein.core.messages.Message; 7 | import org.junit.Assert; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | public class EnvelopeStorageTest { 12 | private Network network = new Network<>(); 13 | private NodeBuilder nb = new NodeBuilder(); 14 | private Random rd = new Random(0); 15 | private Node n0 = new Node(rd, nb); 16 | private Node n1 = new Node(rd, nb); 17 | private Node n2 = new Node(rd, nb); 18 | private Node n3 = new Node(rd, nb); 19 | 20 | private Message dummy = 21 | new Message<>() { 22 | @Override 23 | public void action(Network network, Node from, Node to) {} 24 | }; 25 | 26 | @Before 27 | public void before() { 28 | network.addNode(n0); 29 | network.addNode(n1); 30 | network.addNode(n2); 31 | network.addNode(n3); 32 | } 33 | 34 | @Test 35 | public void testWorkflow() { 36 | Envelope m1 = new Envelope.SingleDestEnvelope<>(dummy, n0, n1, 1, 1); 37 | Envelope m2 = new Envelope.SingleDestEnvelope<>(dummy, n0, n1, 1, 1); 38 | 39 | network.msgs.addMsg(m1); 40 | network.msgs.addMsg(m2); 41 | 42 | Assert.assertNull(network.msgs.peek(2)); 43 | Assert.assertEquals(m2, network.msgs.peek(1)); 44 | Assert.assertEquals(m2, network.msgs.poll(1)); 45 | Assert.assertEquals(m1, network.msgs.poll(1)); 46 | Assert.assertNull(network.msgs.peek(1)); 47 | 48 | Envelope m3 = new Envelope.SingleDestEnvelope<>(dummy, n0, n1, 1, Network.duration + 1); 49 | network.msgs.addMsg(m3); 50 | Assert.assertEquals(2, network.msgs.msgsBySlot.size()); 51 | 52 | network.time = Network.duration + 1; 53 | network.msgs.addMsg(m3); 54 | Assert.assertEquals(1, network.msgs.msgsBySlot.size()); 55 | 56 | network.msgs.clear(); 57 | network.run(1); 58 | } 59 | 60 | @Test 61 | public void testAction() { 62 | AtomicBoolean ab = new AtomicBoolean(false); 63 | Message act = 64 | new Message<>() { 65 | @Override 66 | public void action(Network network, Node from, Node to) { 67 | ab.set(true); 68 | } 69 | }; 70 | 71 | Envelope m = new Envelope.SingleDestEnvelope<>(act, n0, n1, 1, 7 * 1000 + 1); 72 | network.msgs.addMsg(m); 73 | network.run(7); 74 | Assert.assertFalse(ab.get()); 75 | 76 | network.run(1); 77 | Assert.assertTrue(ab.get()); 78 | 79 | ab.set(false); 80 | network.msgs.addMsg(new Envelope.SingleDestEnvelope<>(act, n0, n1, 1, 8 * 1000)); 81 | network.run(1); 82 | Assert.assertTrue(ab.get()); 83 | } 84 | 85 | @Test 86 | public void testMsgArrival() { 87 | AtomicLong ab = new AtomicLong(0); 88 | Message act = 89 | new Message<>() { 90 | @Override 91 | public void action(Network network, Node from, Node to) { 92 | ab.set(EnvelopeStorageTest.this.network.time); 93 | } 94 | }; 95 | 96 | Envelope m = new Envelope.SingleDestEnvelope<>(act, n0, n1, 1, 5); 97 | 98 | network.msgs.addMsg(m); 99 | network.run(1); 100 | 101 | Assert.assertEquals(5, ab.get()); 102 | Assert.assertEquals(0, network.msgs.size()); 103 | } 104 | 105 | @Test 106 | public void testEdgeCase1() { 107 | Assert.assertNull(network.msgs.peek(0)); 108 | Assert.assertNull(network.msgs.peek(10 * 60 * 1000 + 1)); 109 | Envelope m1 = new Envelope.SingleDestEnvelope<>(dummy, n0, n1, 1, 10 * 60 * 1000 + 1); 110 | network.msgs.addMsg(m1); 111 | Assert.assertNotNull(network.msgs.peek(10 * 60 * 1000 + 1)); 112 | } 113 | 114 | @Test 115 | public void testEdgeCase2() { 116 | Assert.assertNull(network.msgs.peek(Network.duration)); 117 | Envelope m1 = new Envelope.SingleDestEnvelope<>(dummy, n0, n1, 1, Network.duration); 118 | network.msgs.addMsg(m1); 119 | Assert.assertNotNull(network.msgs.peek(Network.duration)); 120 | Assert.assertEquals(2, network.msgs.msgsBySlot.size()); 121 | } 122 | 123 | @Test 124 | public void testEdgeCase3() { 125 | Assert.assertNull(network.msgs.peek(Network.duration)); 126 | Envelope m1 = new Envelope.SingleDestEnvelope<>(dummy, n0, n1, 1, Network.duration); 127 | Network.MsgsSlot s = network.msgs.findSlot(59997); 128 | Assert.assertTrue(59997 > s.startTime); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/core/NetworkThroughputTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.Random; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class NetworkThroughputTest { 9 | private final AtomicInteger ai = new AtomicInteger(1); 10 | private final NodeBuilder nb = 11 | new NodeBuilder() { 12 | @Override 13 | public int getX(int rdi) { 14 | return ai.getAndAdd(Node.MAX_X / 2); 15 | } 16 | }; 17 | private final Node n1 = new Node(new Random(0), nb); 18 | private final Node n2 = new Node(new Random(0), nb); 19 | 20 | @Test 21 | public void testRateTCPLimit() { 22 | NetworkLatency nl = new NetworkLatency.NetworkFixedLatency(200 / 2); 23 | NetworkThroughput nt = new NetworkThroughput.MathisNetworkThroughput(nl, 64 * 1024); 24 | 25 | int delay = nt.delay(n1, n2, 0, 2048); 26 | Assert.assertEquals(117, delay); 27 | } 28 | 29 | @Test 30 | public void testRateBandwidthLimit() { 31 | NetworkLatency nl = new NetworkLatency.NetworkFixedLatency(1000); 32 | NetworkThroughput nt = new NetworkThroughput.MathisNetworkThroughput(nl, 5 * 1024 * 1024); 33 | 34 | int delay = nt.delay(n1, n2, 0, 2048); 35 | Assert.assertEquals(1177, delay); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/core/NodeBuilderTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class NodeBuilderTest { 7 | 8 | @Test 9 | public void testRandomPos() { 10 | NodeBuilder nb = new NodeBuilder.NodeBuilderWithRandomPosition(); 11 | 12 | Assert.assertEquals(nb.getX(100), nb.getX(100)); 13 | Assert.assertEquals(nb.getY(100), nb.getY(100)); 14 | 15 | Assert.assertTrue(nb.getY(0) >= 0); 16 | Assert.assertTrue(nb.getY(Integer.MAX_VALUE) >= 0); 17 | 18 | Assert.assertTrue(nb.getY(2077261824) >= 0); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/core/StatsTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.core; 2 | 3 | import java.util.List; 4 | import net.consensys.wittgenstein.core.utils.StatsHelper; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class StatsTest { 9 | 10 | @Test 11 | public void testAvg() { 12 | StatsHelper.SimpleStats s1 = new StatsHelper.SimpleStats(10, 20, 30); 13 | StatsHelper.SimpleStats s2 = new StatsHelper.SimpleStats(16, 26, 36); 14 | 15 | StatsHelper.Stat avg = StatsHelper.avg(List.of(s1, s2)); 16 | Assert.assertTrue(avg instanceof StatsHelper.SimpleStats); 17 | 18 | StatsHelper.SimpleStats avg2 = (StatsHelper.SimpleStats) avg; 19 | Assert.assertEquals(13, avg2.min); 20 | Assert.assertEquals(23, avg2.max); 21 | Assert.assertEquals(33, avg2.avg); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/tools/CSVFormatterTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.tools; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class CSVFormatterTest { 11 | 12 | @Test 13 | public void testValidCSV() { 14 | List fields = Arrays.asList("f1", "f2", "f3", "res"); 15 | Map> map = new HashMap>(); 16 | 17 | Map row1 = new HashMap<>(); 18 | row1.put("f1", "v11"); 19 | row1.put("f2", "v12"); 20 | row1.put("res", 420); 21 | 22 | Map row2 = new HashMap<>(); 23 | row2.put("f2", "v22"); 24 | row2.put("res", 666); 25 | 26 | String expectedOutput = multilineString("f1,f2,f3,res", "v11,v12,,420", ",v22,,666"); 27 | 28 | CSVFormatter formatter = new CSVFormatter(fields); 29 | formatter.add(row1); 30 | formatter.add(row2); 31 | 32 | Assert.assertEquals(expectedOutput, formatter.toString()); 33 | } 34 | 35 | private static String multilineString(String... lines) { 36 | StringBuilder sb = new StringBuilder(); 37 | for (String s : lines) { 38 | sb.append(s); 39 | sb.append('\n'); 40 | } 41 | return sb.toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/tools/CSVLatencyReaderTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.tools; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class CSVLatencyReaderTest { 10 | private CSVLatencyReader reader; 11 | private final String city1 = "city1"; 12 | private final String city2 = "city2"; 13 | private final String city3 = "city3"; 14 | private final String city4 = "city4"; 15 | 16 | @Before 17 | public void setup() { 18 | Map> latencyMatrix = makeLatencyMatrix(); 19 | reader = new CSVLatencyReader(latencyMatrix); 20 | } 21 | 22 | @Test 23 | public void testLoad() { 24 | reader = new CSVLatencyReader(); 25 | Assert.assertTrue(reader.cities().size() > 0); 26 | } 27 | 28 | @Test 29 | public void testSupportedCities() { 30 | Assert.assertTrue(reader.cities().contains(city1)); 31 | Assert.assertTrue(reader.cities().contains(city2)); 32 | Assert.assertTrue(reader.cities().contains(city3)); 33 | Assert.assertFalse(reader.cities().contains(city4)); 34 | } 35 | 36 | private Map> makeLatencyMatrix() { 37 | 38 | Map> latencyMatrix = new HashMap<>(); 39 | 40 | Map c1Map = new HashMap<>(); 41 | c1Map.put(city1, 30f); 42 | c1Map.put(city2, 140f); 43 | c1Map.put(city3, 250f); 44 | 45 | latencyMatrix.put(city1, c1Map); 46 | 47 | Map c2Map = new HashMap<>(); 48 | c2Map.put(city1, 141f); 49 | c2Map.put(city2, 30f); 50 | c2Map.put(city3, 180f); 51 | 52 | Map c3Map = new HashMap<>(); 53 | c3Map.put(city1, 247f); 54 | c3Map.put(city2, 180f); 55 | c3Map.put(city3, 30f); 56 | 57 | latencyMatrix.put(city1, c1Map); 58 | latencyMatrix.put(city2, c2Map); 59 | latencyMatrix.put(city3, c3Map); 60 | latencyMatrix.put(city4, c3Map); 61 | 62 | return latencyMatrix; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/test/java/net/consensys/wittgenstein/tools/NodeDrawerTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.tools; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Random; 8 | import java.util.concurrent.TimeUnit; 9 | import net.consensys.wittgenstein.core.Network; 10 | import net.consensys.wittgenstein.core.Node; 11 | import net.consensys.wittgenstein.core.NodeBuilder; 12 | import net.consensys.wittgenstein.core.Protocol; 13 | import org.junit.Assert; 14 | import org.junit.Test; 15 | 16 | public class NodeDrawerTest { 17 | 18 | @Test 19 | public void testDrawImage() throws IOException { 20 | NodeBuilder nb = new NodeBuilder.NodeBuilderWithRandomPosition(); 21 | Random rd = new Random(); 22 | 23 | Protocol p1 = 24 | new Protocol() { 25 | Network network = new Network<>(); 26 | 27 | @Override 28 | public Network network() { 29 | return network; 30 | } 31 | 32 | @Override 33 | public Protocol copy() { 34 | return null; 35 | } 36 | 37 | @Override 38 | public void init() {} 39 | }; 40 | 41 | NodeDrawer.NodeStatus nds = 42 | new NodeDrawer.NodeStatus() { 43 | @Override 44 | public int getVal(Node n) { 45 | return 5; 46 | } 47 | 48 | @Override 49 | public boolean isSpecial(Node n) { 50 | return false; 51 | } 52 | 53 | @Override 54 | public int getMax() { 55 | return 5; 56 | } 57 | 58 | @Override 59 | public int getMin() { 60 | return 0; 61 | } 62 | }; 63 | List nodes = new ArrayList<>(); 64 | nodes.add(new Node(rd, nb)); 65 | nodes.add(new Node(rd, nb)); 66 | 67 | p1.init(); 68 | p1.network().runMs(200); 69 | File destAnim = File.createTempFile(this.getClass().getSimpleName(), "1"); 70 | destAnim.delete(); 71 | 72 | File destImg = File.createTempFile(this.getClass().getSimpleName(), "2"); 73 | destImg.delete(); 74 | 75 | NodeDrawer nd = new NodeDrawer(nds, destAnim, 1); 76 | nd.drawNewState(100, TimeUnit.MILLISECONDS, nodes); 77 | nd.writeLastToGif(destImg); 78 | Assert.assertTrue(destImg.exists()); 79 | destImg.delete(); 80 | 81 | nd.close(); 82 | Assert.assertTrue(destAnim.exists()); 83 | destAnim.delete(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /protocols/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.johnrengelman.shadow" 3 | } 4 | 5 | dependencies { 6 | implementation project(':core') 7 | } 8 | 9 | shadowJar { 10 | archiveName = "wittgenstein-all.jar" 11 | } 12 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/OptimisticP2PSignatureScenarios.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import java.util.List; 4 | import net.consensys.wittgenstein.core.NetworkLatency; 5 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 6 | import net.consensys.wittgenstein.core.RunMultipleTimes; 7 | import net.consensys.wittgenstein.core.utils.StatsHelper; 8 | import net.consensys.wittgenstein.protocols.OptimisticP2PSignature.OptimisticP2PSignatureParameters; 9 | 10 | @SuppressWarnings("SameParameterValue") 11 | public class OptimisticP2PSignatureScenarios { 12 | 13 | static class BasicStats { 14 | final int doneAtMin; 15 | final int doneAtAvg; 16 | final int doneAtMax; 17 | 18 | final int msgRcvMin; 19 | final int msgRcvAvg; 20 | final int msgRcvMax; 21 | 22 | BasicStats( 23 | long doneAtMin, 24 | long doneAtAvg, 25 | long doneAtMax, 26 | long msgRcvMin, 27 | long msgRcvAvg, 28 | long msgRcvMax) { 29 | this.doneAtMin = (int) doneAtMin; 30 | this.doneAtAvg = (int) doneAtAvg; 31 | this.doneAtMax = (int) doneAtMax; 32 | this.msgRcvMin = (int) msgRcvMin; 33 | this.msgRcvAvg = (int) msgRcvAvg; 34 | this.msgRcvMax = (int) msgRcvMax; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "; doneAtAvg=" + doneAtAvg + "; msgRcvAvg=" + msgRcvAvg; 40 | } 41 | } 42 | 43 | private BasicStats run(int rounds, OptimisticP2PSignatureParameters params) { 44 | List stats = 45 | List.of(new StatsHelper.DoneAtStatGetter(), new StatsHelper.MsgReceivedStatGetter()); 46 | RunMultipleTimes rmt = 47 | new RunMultipleTimes<>(new OptimisticP2PSignature(params), rounds, 0, stats, null); 48 | List res = rmt.run(RunMultipleTimes.contUntilDone()); 49 | 50 | return new BasicStats( 51 | res.get(0).get("min"), 52 | res.get(0).get("avg"), 53 | res.get(0).get("max"), 54 | res.get(1).get("min"), 55 | res.get(1).get("avg"), 56 | res.get(1).get("max")); 57 | } 58 | 59 | private void logErrors(Double errorRate) { 60 | boolean printed = false; 61 | 62 | double[] errors = new double[] {00}; 63 | if (errorRate != null) { 64 | errors = new double[] {errorRate}; 65 | } 66 | 67 | for (int i = 0; i < errors.length; i++) { 68 | int e = (int) (errors[i] * 100); 69 | } 70 | 71 | for (int i = 0; i < errors.length; i++) { 72 | double e = errors[i]; 73 | for (int n = 128; n <= 4096; n *= 2) { 74 | OptimisticP2PSignatureParameters params = defaultParams(n, errors[i], null, null, null); 75 | 76 | if (!printed) { 77 | System.out.println("\nBehavior when the number of nodes increases - " + params); 78 | printed = true; 79 | } 80 | 81 | BasicStats bs = run(n > 9000 ? 2 : n < 1000 ? 1 : 1, params); 82 | System.out.println(n + " nodes: " + e + bs); 83 | System.out.flush(); 84 | } 85 | } 86 | } 87 | 88 | private static OptimisticP2PSignatureParameters defaultParams( 89 | int nodes, 90 | Double deadRatio, 91 | Integer connectionCount, 92 | Double tor, 93 | RegistryNodeBuilders.Location loc) { 94 | 95 | int ts = (int) (nodes * 0.99); 96 | 97 | String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.CITIES, true, 0); 98 | String nl = NetworkLatency.NetworkLatencyByCityWJitter.class.getSimpleName(); 99 | 100 | return new OptimisticP2PSignatureParameters(nodes, ts, 3, 4, nb, nl); 101 | } 102 | 103 | public static void main(String... args) { 104 | OptimisticP2PSignatureScenarios scenario = new OptimisticP2PSignatureScenarios(); 105 | scenario.logErrors(null); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/PingPong.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.*; 4 | import net.consensys.wittgenstein.core.messages.Message; 5 | 6 | /** A simulation of a trivial protocol to be used as a sample. */ 7 | public class PingPong implements Protocol { 8 | final PingPongParameters params; 9 | 10 | /** You need a network. Nodes are added to this network. Network latency can be set later. */ 11 | private final Network network = new Network<>(); 12 | 13 | /** Nodes have positions. This position is chosen by the builder. */ 14 | private final NodeBuilder nb; 15 | 16 | /** 17 | * Messages, exchanged on the network, are specific to the protocol. Here we have two messages: 18 | * Ping & Pong. 19 | */ 20 | static class Ping extends Message { 21 | @Override 22 | public void action(Network network, PingPongNode from, PingPongNode to) { 23 | to.onPing(from); 24 | } 25 | } 26 | 27 | static class Pong extends Message { 28 | @Override 29 | public void action(Network network, PingPongNode from, PingPongNode to) { 30 | to.onPong(); 31 | } 32 | } 33 | 34 | public static class PingPongParameters extends WParameters { 35 | final int nodeCt; 36 | final String nodeBuilderName; 37 | final String networkLatencyName; 38 | 39 | public PingPongParameters() { 40 | nodeCt = 1000; 41 | nodeBuilderName = null; 42 | networkLatencyName = null; 43 | } 44 | 45 | public PingPongParameters(int nodeCt, String nodeBuilderName, String networkLatencyName) { 46 | this.nodeCt = nodeCt; 47 | this.nodeBuilderName = nodeBuilderName; 48 | this.networkLatencyName = networkLatencyName; 49 | } 50 | } 51 | 52 | public PingPong(PingPongParameters params) { 53 | this.params = params; 54 | this.nb = RegistryNodeBuilders.singleton.getByName(params.nodeBuilderName); 55 | this.network.setNetworkLatency( 56 | RegistryNetworkLatencies.singleton.getByName(params.networkLatencyName)); 57 | } 58 | 59 | /** Nodes are specialized for the protocol. */ 60 | class PingPongNode extends Node { 61 | int pong; 62 | 63 | PingPongNode() { 64 | super(network.rd, nb); 65 | } 66 | 67 | void onPing(PingPongNode from) { 68 | network.send(new Pong(), this, from); 69 | } 70 | 71 | void onPong() { 72 | pong++; 73 | } 74 | } 75 | 76 | @Override 77 | public PingPong copy() { 78 | return new PingPong(params); 79 | } 80 | 81 | @Override 82 | public void init() { 83 | for (int i = 0; i < params.nodeCt; i++) { 84 | network.addNode(new PingPongNode()); 85 | } 86 | network.sendAll(new Ping(), network.getNodeById(0)); 87 | } 88 | 89 | @Override 90 | public Network network() { 91 | return network; 92 | } 93 | 94 | public static void main(String... args) { 95 | PingPong p = new PingPong(new PingPongParameters()); 96 | p.init(); 97 | for (int i = 0; i < 500; i += 50) { 98 | System.out.println(i + " ms, pongs received " + p.network.getNodeById(0).pong); 99 | p.network.runMs(50); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/ethpow/ETHAgentMiner.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols.ethpow; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.FileWriter; 5 | import java.io.PrintWriter; 6 | import java.util.LinkedList; 7 | import java.util.ListIterator; 8 | import net.consensys.wittgenstein.core.BlockChainNetwork; 9 | import net.consensys.wittgenstein.core.NodeBuilder; 10 | 11 | @SuppressWarnings("WeakerAccess") 12 | public class ETHAgentMiner extends ETHMiner { 13 | private static final String DATA_FILE = "decisions.csv"; 14 | 15 | /** List of the decision taken that we need to evaluate. Sorted by evaluation height. */ 16 | final LinkedList decisions = new LinkedList<>(); 17 | 18 | private final PrintWriter decisionOutput; 19 | 20 | public ETHAgentMiner( 21 | BlockChainNetwork network, 22 | NodeBuilder nb, 23 | int hashPower, 24 | ETHPoW.POWBlock genesis) { 25 | super(network, nb, hashPower, genesis); 26 | try { 27 | FileWriter fw = new FileWriter(DATA_FILE, true); 28 | BufferedWriter bw = new BufferedWriter(fw); 29 | decisionOutput = new PrintWriter(bw); 30 | } catch (Throwable e) { 31 | throw new IllegalStateException(e); 32 | } 33 | } 34 | 35 | /** Add a decision tp the list of decisions to be evaluated. */ 36 | protected final void addDecision(ETHPoW.Decision d) { 37 | if (d.rewardAtHeight <= head.height) { 38 | throw new IllegalArgumentException("Can't calculate a reward for " + d + ", head=" + head); 39 | } 40 | 41 | if (decisions.isEmpty() || decisions.peekLast().rewardAtHeight <= d.rewardAtHeight) { 42 | decisions.addLast(d); 43 | } else { 44 | ListIterator it = decisions.listIterator(decisions.size()); 45 | while (it.hasPrevious()) { 46 | if (it.previous().rewardAtHeight <= d.rewardAtHeight) { 47 | it.next(); 48 | break; 49 | } 50 | } 51 | it.add(d); 52 | } 53 | } 54 | 55 | @Override 56 | protected final void onNewHead(ETHPoW.POWBlock oldHead, ETHPoW.POWBlock newHead) { 57 | while (!decisions.isEmpty() && decisions.peekFirst().rewardAtHeight <= newHead.height) { 58 | ETHPoW.Decision cur = decisions.pollFirst(); 59 | assert cur != null; 60 | double reward = cur.reward(newHead, this); 61 | String toWrite = cur.forCSV() + "," + reward + "\n"; 62 | decisionOutput.write(toWrite); 63 | } 64 | } 65 | 66 | @Override 67 | public void close() { 68 | decisionOutput.close(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/ethpow/ETHSelfishMiner2.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols.ethpow; 2 | 3 | import net.consensys.wittgenstein.core.BlockChainNetwork; 4 | import net.consensys.wittgenstein.core.NodeBuilder; 5 | import net.consensys.wittgenstein.core.RegistryNetworkLatencies; 6 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 7 | 8 | /** 9 | * Implementation of the algo proposed by Ittay Eyal and Emin Gun Sirer in 10 | * https://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf (algorithm 1, page 6) 11 | */ 12 | public class ETHSelfishMiner2 extends ETHMiner { 13 | private ETHPoW.POWBlock privateMinerBlock; 14 | private ETHPoW.POWBlock otherMinersHead = genesis; 15 | 16 | public ETHSelfishMiner2( 17 | BlockChainNetwork network, 18 | NodeBuilder nb, 19 | int hashPower, 20 | ETHPoW.POWBlock genesis) { 21 | super(network, nb, hashPower, genesis); 22 | } 23 | 24 | private int privateHeight() { 25 | return privateMinerBlock == null ? 0 : privateMinerBlock.height; 26 | } 27 | 28 | @Override 29 | protected boolean sendMinedBlock(ETHPoW.POWBlock mined) { 30 | return false; 31 | } 32 | 33 | protected boolean includeUncle(ETHPoW.POWBlock uncle) { 34 | return true; 35 | } 36 | 37 | @Override 38 | protected void onMinedBlock(ETHPoW.POWBlock mined) { 39 | if (privateMinerBlock != null && mined.height <= privateMinerBlock.height) { 40 | throw new IllegalStateException( 41 | "privateMinerBlock=" + privateMinerBlock + ", mined=" + mined); 42 | } 43 | privateMinerBlock = mined; 44 | 45 | int deltaP = privateHeight() - (otherMinersHead.height - 1); 46 | if (deltaP == 0 && depth(privateMinerBlock) == 2) { 47 | otherMinersHead = best(otherMinersHead, privateMinerBlock); 48 | sendAllMined(); 49 | } 50 | 51 | startNewMining(privateMinerBlock); 52 | } 53 | 54 | @Override 55 | protected void onReceivedBlock(ETHPoW.POWBlock rcv) { 56 | otherMinersHead = best(otherMinersHead, rcv); 57 | if (otherMinersHead != rcv) { 58 | // Nothing to do if their head doesn't change. 59 | return; 60 | } 61 | 62 | if (head == rcv) { 63 | // They won => We move to their chain 64 | sendAllMined(); 65 | startNewMining(head); 66 | } else { 67 | // We're ahead. 68 | ETHPoW.POWBlock toSend = privateMinerBlock; 69 | while (toSend.parent != null 70 | && toSend.height >= rcv.height 71 | && toSend.parent.totalDifficulty.compareTo(rcv.totalDifficulty) > 0) { 72 | toSend = toSend.parent; 73 | } 74 | 75 | while (toSend != null && toSend.producer == this && minedToSend.contains(toSend)) { 76 | otherMinersHead = best(otherMinersHead, toSend); 77 | sendBlock(toSend); 78 | toSend = toSend.parent; 79 | } 80 | } 81 | } 82 | 83 | public static void main(String... args) { 84 | final int runs = 1; 85 | final int hours = 518; 86 | final String bdlName = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 87 | 88 | for (RegistryNetworkLatencies.Type type : 89 | new RegistryNetworkLatencies.Type[] { 90 | RegistryNetworkLatencies.Type.FIXED, RegistryNetworkLatencies.Type.UNIFORM 91 | }) { 92 | for (int time : new int[] {2000, 4000, 8000}) { 93 | final String nlName = RegistryNetworkLatencies.name(type, time); 94 | final double[] pows = new double[] {0.01, 0.1, 0.2, 0.25, 0.3, 0.35, 0.40, 0.45, 0.50}; 95 | 96 | // Merge the two results with: 97 | // paste -d ',' bad.txt good.txt | awk -F "," '{ print $2 ", " $5 ", " $3 ", " $4 ", " $12 98 | // ", " $11 ", " $6 ", " $13 ", " $7 ", " $14 }' 99 | ETHMiner.tryMiner(bdlName, nlName, ETHSelfishMiner2.class, pows, hours, runs); 100 | // ETHMiner.tryMiner(bdlName, nlName, ETHMiner.class, pows, hours, runs); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/handeleth2/AggToVerify.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols.handeleth2; 2 | 3 | import java.util.List; 4 | 5 | class AggToVerify { 6 | final int from; 7 | final int height; 8 | final int ownHash; 9 | final int rank; 10 | final List attestations; 11 | final int level; 12 | 13 | int getRank() { 14 | return rank; 15 | } 16 | 17 | AggToVerify(int from, int level, int ownHash, int rank, List attestations) { 18 | if (level <= 0 || from < 0 || ownHash < 0 || attestations.isEmpty()) { 19 | throw new IllegalArgumentException(); 20 | } 21 | this.from = from; 22 | this.ownHash = ownHash; 23 | this.rank = rank; 24 | this.attestations = attestations; 25 | this.level = level; 26 | this.height = attestations.get(0).height; 27 | 28 | for (Attestation a : attestations) { 29 | if (a.height != height) { 30 | throw new IllegalArgumentException("bad attestation list:" + attestations); 31 | } 32 | } 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "AggToVerify{" 38 | + "from=" 39 | + from 40 | + ", ownHash=" 41 | + ownHash 42 | + ", rank=" 43 | + rank 44 | + ", level=" 45 | + level 46 | + '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/handeleth2/Attestation.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols.handeleth2; 2 | 3 | import java.util.BitSet; 4 | 5 | /** An attestation is for a given height and a given block hash. */ 6 | public class Attestation { 7 | // The height for this attestation 8 | final int height; 9 | // The hash we're attesting for 10 | final int hash; 11 | // Who is attesting 12 | final BitSet who; 13 | 14 | // An initial attestation, with a single attester. 15 | public Attestation(int height, int hash, int who) { 16 | this.height = height; 17 | this.hash = hash; 18 | this.who = new BitSet(); 19 | this.who.set(who); 20 | } 21 | 22 | public Attestation(Attestation base, BitSet whoToCopy) { 23 | this.height = base.height; 24 | this.hash = base.hash; 25 | this.who = (BitSet) whoToCopy.clone(); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "{" + "height=" + height + ", hash=" + hash + ", who=" + who + '}'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/handeleth2/HandelEth2Parameters.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols.handeleth2; 2 | 3 | import net.consensys.wittgenstein.core.WParameters; 4 | 5 | public class HandelEth2Parameters extends WParameters { 6 | public static final int PERIOD_TIME = 6000; 7 | public static final int PERIOD_AGG_TIME = PERIOD_TIME * 3; 8 | 9 | /** The number of nodes in the network */ 10 | final int nodeCount; 11 | 12 | /** The minimum time it takes to do a pairing for a node. */ 13 | final int pairingTime; 14 | 15 | int levelWaitTime; 16 | final int periodDurationMs; 17 | 18 | final int nodesDown; 19 | 20 | final String nodeBuilderName; 21 | final String networkLatencyName; 22 | 23 | /** 24 | * Allows to test what happens when all the nodes are not starting at the same time. If the value 25 | * is different than 0, all the nodes are starting at a time uniformly distributed between zero 26 | * and 'desynchronizedStart' 27 | */ 28 | int desynchronizedStart; 29 | 30 | // Used for json / http server 31 | @SuppressWarnings("unused") 32 | public HandelEth2Parameters() { 33 | this.nodeCount = 64; 34 | this.pairingTime = 3; 35 | this.levelWaitTime = 100; 36 | this.periodDurationMs = 50; 37 | this.nodesDown = 0; 38 | this.nodeBuilderName = null; 39 | this.networkLatencyName = null; 40 | this.desynchronizedStart = 0; 41 | } 42 | 43 | public HandelEth2Parameters( 44 | int nodeCount, 45 | int pairingTime, 46 | int levelWaitTime, 47 | int periodDurationMs, 48 | int nodesDown, 49 | String nodeBuilderName, 50 | String networkLatencyName, 51 | int desynchronizedStart) { 52 | 53 | if (nodesDown >= nodeCount || nodesDown < 0) { 54 | throw new IllegalArgumentException("nodeCount=" + nodeCount); 55 | } 56 | if (Integer.bitCount(nodeCount) != 1) { 57 | throw new IllegalArgumentException("We support only power of two nodes in this simulation"); 58 | } 59 | 60 | this.nodeCount = nodeCount; 61 | this.pairingTime = pairingTime; 62 | this.levelWaitTime = levelWaitTime; 63 | this.periodDurationMs = periodDurationMs; 64 | this.nodesDown = nodesDown; 65 | this.nodeBuilderName = nodeBuilderName; 66 | this.networkLatencyName = networkLatencyName; 67 | this.desynchronizedStart = desynchronizedStart; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /protocols/src/main/java/net/consensys/wittgenstein/protocols/handeleth2/SendAggregation.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols.handeleth2; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import net.consensys.wittgenstein.core.Network; 6 | import net.consensys.wittgenstein.core.messages.Message; 7 | 8 | /** The only message exchanged by the aggregating participants. */ 9 | class SendAggregation extends Message { 10 | 11 | /** 12 | * The height: as we can run multiple aggregations for multiple heights in parallel, this allows 13 | * to identify what we are actually aggregating. 14 | */ 15 | final int height; 16 | 17 | /** The level: each participant sends aggregation for a given level in the communication tree. */ 18 | final int level; 19 | 20 | /** 21 | * In Ethereum 2, different participants can have different views of the current block for a given 22 | * height: this is represented by you own hash. In Handel you must include your individual 23 | * contribution for the hash you're attesting for. 24 | */ 25 | final int ownHash; 26 | 27 | /** As a participant, se send all the attestations we received for a given height. */ 28 | final List attestations; 29 | 30 | /** 31 | * A flag to say that you have finished this level and that the receiver should not contact you. 32 | * It could also be used to signal that you reached the threshold or you're exiting for any 33 | * reason, i.e. the receiver is wasting his time if he tries to contact you 34 | */ 35 | final boolean levelFinished; 36 | 37 | SendAggregation(int level, int ownHash, boolean levelFinished, List attestations) { 38 | if (attestations.isEmpty()) { 39 | throw new IllegalArgumentException("attestations should not be empty"); 40 | } 41 | this.attestations = attestations; 42 | this.height = attestations.get(0).height; 43 | this.level = level; 44 | this.ownHash = ownHash; 45 | this.levelFinished = levelFinished; 46 | 47 | boolean foundHash = false; 48 | for (Attestation a : attestations) { 49 | if (a.height != height) { 50 | throw new IllegalStateException("bad height:" + attestations); 51 | } 52 | if (a.hash == ownHash) { 53 | foundHash = true; 54 | } 55 | } 56 | if (!foundHash) { 57 | throw new IllegalStateException("no attestation with your own hash?"); 58 | } 59 | } 60 | 61 | // For tests 62 | SendAggregation(int level, int ownHash, boolean levelFinished, Attestation attestation) { 63 | this(level, ownHash, levelFinished, Collections.singletonList(attestation)); 64 | } 65 | 66 | @Override 67 | public void action(Network network, HNode from, HNode to) { 68 | to.onNewAgg(from, this); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/CasperByzantineTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.NetworkLatency; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class CasperByzantineTest { 8 | private final CasperIMD ci = 9 | new CasperIMD(new CasperIMD.CasperParemeters(1, false, 2, 2, 1000, 1, null, null)); 10 | 11 | @Test 12 | public void testByzantineWF() { 13 | ci.network.networkLatency = new NetworkLatency.NetworkNoLatency(); 14 | 15 | CasperIMD.ByzBlockProducerWF byz = ci.new ByzBlockProducerWF(0, ci.genesis); 16 | ci.init(byz); 17 | 18 | ci.network.run(9); 19 | Assert.assertEquals(ci.genesis, ci.network.observer.head); 20 | 21 | ci.network.run(1); // 10 seconds: 8 for start + 1 for build time + 1 for network delay 22 | Assert.assertNotEquals(ci.genesis, ci.network.observer.head); 23 | Assert.assertEquals(1, ci.network.observer.head.height); 24 | Assert.assertEquals(byz, ci.network.observer.head.producer); 25 | 26 | ci.network.run(8); // 18s : 16s to start + build time + network delay 27 | Assert.assertNotEquals(ci.genesis, ci.network.observer.head); 28 | Assert.assertEquals(2, ci.network.observer.head.height); 29 | Assert.assertNotEquals(byz, ci.network.observer.head.producer); 30 | 31 | ci.network.run(8); // 26s : 24 (because of the delay) + build + network 32 | Assert.assertNotEquals(ci.genesis, ci.network.observer.head); 33 | Assert.assertEquals(3, ci.network.observer.head.height); 34 | Assert.assertEquals(byz, ci.network.observer.head.producer); 35 | } 36 | 37 | @Test 38 | public void testByzantineWFWithDelay() { 39 | ci.network.networkLatency = new NetworkLatency.NetworkNoLatency(); 40 | 41 | CasperIMD.ByzBlockProducerWF byz = ci.new ByzBlockProducerWF(-2000, ci.genesis); 42 | ci.init(byz); 43 | 44 | ci.network.run(5); 45 | Assert.assertEquals(0, byz.head.height); 46 | 47 | ci.network.run(1); 48 | Assert.assertEquals(1, byz.head.height); 49 | Assert.assertEquals(0, ci.network.observer.head.height); 50 | 51 | ci.network.run(2); // The observer will wait for the right time, hence 8 seconds 52 | Assert.assertEquals(1, ci.network.observer.head.height); 53 | 54 | ci.network.run(9); // 18s : 16s to start + build time + network delay 55 | Assert.assertEquals(1, ci.network.observer.head.height); 56 | ci.network.run(1); // 18s : 16s to start + build time + network delay 57 | Assert.assertEquals(2, byz.head.height); 58 | assert byz.head.producer != null; 59 | Assert.assertNotEquals(byz, byz.head.producer); 60 | 61 | ci.network.run(3); 62 | Assert.assertEquals(2, byz.head.height); 63 | ci.network.run(1); // 22s: 24 -2 seconds delay 64 | Assert.assertEquals(3, byz.head.height); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/DfinityTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.NetworkLatency; 4 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | // add more tests 10 | public class DfinityTest { 11 | private String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 12 | private String nl = NetworkLatency.NetworkNoLatency.class.getSimpleName(); 13 | private final Dfinity dfinity = 14 | new Dfinity(new Dfinity.DfinityParameters(10, 10, 10, 1, 1, 0, nb, nl)); 15 | 16 | @Before 17 | public void before() { 18 | dfinity.network.networkLatency = new NetworkLatency.NetworkNoLatency(); 19 | dfinity.init(); 20 | } 21 | 22 | @Test 23 | public void testRun() { 24 | dfinity.network.run(11); 25 | Assert.assertEquals(3, dfinity.network.observer.head.height); 26 | } 27 | 28 | // TODO @Test 29 | public void testCopy() { 30 | Dfinity p1 = new Dfinity(new Dfinity.DfinityParameters(10, 50, 25, 100, 1, 5, nb, nl)); 31 | Dfinity p2 = p1.copy(); 32 | p1.init(); 33 | p2.init(); 34 | 35 | while (p1.network.time < 20000) { 36 | p1.network().runMs(1); 37 | p2.network().runMs(1); 38 | Assert.assertEquals(p1.network.msgs.size(), p2.network.msgs.size()); 39 | for (Dfinity.DfinityNode n1 : p1.network().allNodes) { 40 | Dfinity.DfinityNode n2 = p2.network().getNodeById(n1.nodeId); 41 | Assert.assertNotNull(n2); 42 | Assert.assertEquals(n1.isDown(), n2.isDown()); 43 | Assert.assertEquals(n1.head.proposalTime, n2.head.proposalTime); 44 | Assert.assertEquals(n1.committeeMajorityHeight, n2.committeeMajorityHeight); 45 | Assert.assertEquals("" + n1, n1.committeeMajorityBlocks, n2.committeeMajorityBlocks); 46 | Assert.assertEquals(n1.lastRandomBeacon, n2.lastRandomBeacon); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/ENRGossipingTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.function.Predicate; 6 | import net.consensys.wittgenstein.core.*; 7 | import net.consensys.wittgenstein.core.utils.StatsHelper; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | public class ENRGossipingTest { 12 | 13 | // test: runs until all nodes have found at least 1 peer with a matching capability. 14 | 15 | // Test that copy method works 16 | @Test 17 | public void testCopy() { 18 | String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 19 | String nl = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 20 | ENRGossiping p1 = 21 | new ENRGossiping( 22 | new ENRGossiping.ENRParameters(100, 10, 25, 15000, 2, 20, 0.4f, 10, 5, 5, nb, nl)); 23 | ENRGossiping p2 = p1.copy(); 24 | p1.init(); 25 | p1.network().run(10); 26 | p2.init(); 27 | p2.network().run(10); 28 | 29 | for (ENRGossiping.ETHNode n1 : p1.network().allNodes) { 30 | ENRGossiping.ETHNode n2 = p2.network().getNodeById(n1.nodeId); 31 | Assert.assertNotNull(n2); 32 | Assert.assertEquals(n1.doneAt, n2.doneAt); 33 | Assert.assertEquals(n1.isDown(), n2.isDown()); 34 | Assert.assertEquals(n1.getMsgReceived(-1).size(), n2.getMsgReceived(-1).size()); 35 | Assert.assertEquals(n1.x, n2.x); 36 | Assert.assertEquals(n1.y, n2.y); 37 | Assert.assertEquals(n1.peers, n2.peers); 38 | } 39 | } 40 | 41 | @Test 42 | public void testPPT() { 43 | String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 44 | 45 | String nl = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 46 | ENRGossiping p1 = 47 | new ENRGossiping( 48 | new ENRGossiping.ENRParameters(100, 10, 25, 15000, 2, 20, 0.4f, 30, 10, 5, nb, nl)); 49 | Predicate contIf = pp1 -> pp1.network().time <= 1000 * 100; 50 | StatsHelper.StatsGetter sg = 51 | new StatsHelper.StatsGetter() { 52 | final List fields = new StatsHelper.SimpleStats(0, 0, 0).fields(); 53 | 54 | @Override 55 | public List fields() { 56 | return fields; 57 | } 58 | 59 | @Override 60 | public StatsHelper.Stat get(List liveNodes) { 61 | return StatsHelper.getStatsOn(liveNodes, n -> ((ENRGossiping.ETHNode) n).doneAt); 62 | } 63 | }; 64 | ProgressPerTime ppp = 65 | new ProgressPerTime( 66 | p1, 67 | "", 68 | "Nodes that have found capabilities", 69 | sg, 70 | 1, 71 | null, 72 | 10000, 73 | TimeUnit.MILLISECONDS); 74 | ppp.run(contIf); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/HandelTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.NetworkLatency; 4 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 5 | import net.consensys.wittgenstein.core.RunMultipleTimes; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | public class HandelTest { 10 | private String nl = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 11 | private String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 12 | 13 | @Test 14 | public void testCopy() { 15 | Handel p1 = 16 | new Handel( 17 | new Handel.HandelParameters( 18 | 64, 60, 6, 10, 5, 5, 10, 2, nb, nl, 100, false, false, null)); 19 | Handel p2 = p1.copy(); 20 | p1.init(); 21 | p2.init(); 22 | 23 | while (p1.network().time < 2000) { 24 | p1.network().runMs(1); 25 | p2.network().runMs(1); 26 | Assert.assertEquals(p1.network().msgs.size(), p2.network().msgs.size()); 27 | for (Handel.HNode n1 : p1.network().allNodes) { 28 | Handel.HNode n2 = p2.network().getNodeById(n1.nodeId); 29 | Assert.assertNotNull(n2); 30 | Assert.assertEquals(n1.doneAt, n2.doneAt); 31 | Assert.assertEquals(n1.totalSigSize(), n2.totalSigSize()); 32 | } 33 | } 34 | } 35 | 36 | @Test 37 | public void testRun() { 38 | Handel p1 = 39 | new Handel( 40 | new Handel.HandelParameters( 41 | 64, 60, 6, 10, 5, 5, 10, 2, nb, nl, 100, false, false, null)); 42 | 43 | p1.init(); 44 | for (; RunMultipleTimes.contUntilDone().test(p1) && p1.network().time < 20000; ) { 45 | p1.network().runMs(1000); 46 | } 47 | 48 | Assert.assertFalse(RunMultipleTimes.contUntilDone().test(p1)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/OptimisticP2PSignatureTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.NetworkLatency; 4 | import net.consensys.wittgenstein.core.Node; 5 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | public class OptimisticP2PSignatureTest { 10 | private String nl = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 11 | private String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 12 | 13 | @Test 14 | public void testSimple() { 15 | int nCt = 100; 16 | OptimisticP2PSignature p = 17 | new OptimisticP2PSignature( 18 | new OptimisticP2PSignature.OptimisticP2PSignatureParameters( 19 | nCt, nCt / 2 + 1, 13, 3, nb, nl)); 20 | p.init(); 21 | p.network().run(10); 22 | 23 | Assert.assertEquals(nCt, p.network().allNodes.size()); 24 | for (Node nc : p.network().allNodes) { 25 | OptimisticP2PSignature.P2PSigNode n = (OptimisticP2PSignature.P2PSigNode) nc; 26 | Assert.assertFalse(n.isDown()); 27 | Assert.assertTrue(n.doneAt > 0); 28 | Assert.assertTrue(n.done); 29 | Assert.assertTrue(n.verifiedSignatures.cardinality() > nCt / 2); 30 | } 31 | } 32 | 33 | @Test 34 | public void testCopy() { 35 | OptimisticP2PSignature p1 = 36 | new OptimisticP2PSignature( 37 | new OptimisticP2PSignature.OptimisticP2PSignatureParameters(200, 160, 10, 2, nb, nl)); 38 | OptimisticP2PSignature p2 = p1.copy(); 39 | p1.init(); 40 | p1.network().runMs(200); 41 | p2.init(); 42 | p2.network().runMs(200); 43 | 44 | for (OptimisticP2PSignature.P2PSigNode n1 : p1.network().allNodes) { 45 | OptimisticP2PSignature.P2PSigNode n2 = p2.network().getNodeById(n1.nodeId); 46 | Assert.assertNotNull(n2); 47 | Assert.assertEquals(n1.done, n2.done); 48 | Assert.assertEquals(n1.doneAt, n2.doneAt); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/P2PFloodTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.NetworkLatency; 4 | import net.consensys.wittgenstein.core.Node; 5 | import net.consensys.wittgenstein.core.Protocol; 6 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class P2PFloodTest { 11 | 12 | @Test 13 | public void testSimpleRun() { 14 | String nl = NetworkLatency.NetworkNoLatency.class.getSimpleName(); 15 | String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 16 | 17 | P2PFlood po = new P2PFlood(new P2PFlood.P2PFloodParameters(100, 10, 50, 1, 1, 10, 30, nb, nl)); 18 | Protocol p = po.copy(); 19 | p.init(); 20 | p.network().run(20); 21 | po.init(); 22 | 23 | Assert.assertEquals(100, p.network().allNodes.size()); 24 | for (Node nn : p.network().allNodes) { 25 | P2PFlood.P2PFloodNode n = (P2PFlood.P2PFloodNode) nn; 26 | if (n.isDown()) { 27 | Assert.assertEquals(0, n.getMsgReceived(-1).size()); 28 | } else { 29 | Assert.assertEquals(1, n.getMsgReceived(-1).size()); 30 | } 31 | } 32 | } 33 | 34 | @Test 35 | public void testLongRun() { 36 | String nl = NetworkLatency.AwsRegionNetworkLatency.class.getSimpleName(); 37 | String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.AWS, true, 0); 38 | 39 | Protocol po = 40 | new P2PFlood(new P2PFlood.P2PFloodParameters(4500, 4000, 500, 1, 1, 50, 300, nb, nl)); 41 | Protocol p = po.copy(); 42 | p.init(); 43 | p.network().run(2000); 44 | po.init(); 45 | 46 | Assert.assertEquals(4500, p.network().allNodes.size()); 47 | for (Node nn : p.network().allNodes) { 48 | P2PFlood.P2PFloodNode n = (P2PFlood.P2PFloodNode) nn; 49 | if (n.isDown()) { 50 | Assert.assertEquals(0, n.getMsgReceived(-1).size()); 51 | } else { 52 | Assert.assertEquals(1, n.getMsgReceived(-1).size()); 53 | } 54 | } 55 | } 56 | 57 | @Test 58 | public void testCopy() { 59 | String nb = RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 60 | String nl = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 61 | P2PFlood p1 = new P2PFlood(new P2PFlood.P2PFloodParameters(2000, 10, 50, 1, 1, 10, 30, nb, nl)); 62 | P2PFlood p2 = p1.copy(); 63 | p1.init(); 64 | p1.network().runMs(1000); 65 | p2.init(); 66 | p2.network().runMs(1000); 67 | 68 | for (P2PFlood.P2PFloodNode n1 : p1.network().allNodes) { 69 | P2PFlood.P2PFloodNode n2 = p2.network().getNodeById(n1.nodeId); 70 | Assert.assertNotNull(n2); 71 | Assert.assertEquals(n1.doneAt, n2.doneAt); 72 | Assert.assertEquals(n1.isDown(), n2.isDown()); 73 | Assert.assertEquals(n1.getMsgReceived(-1).size(), n2.getMsgReceived(-1).size()); 74 | Assert.assertEquals(n1.x, n2.x); 75 | Assert.assertEquals(n1.y, n2.y); 76 | Assert.assertEquals(n1.peers, n2.peers); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/PaxosTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class PaxosTest { 7 | 8 | @Test 9 | public void testSimple() { 10 | Paxos p = new Paxos(new Paxos.PaxosParameters(3, 1, 1000, null, null)); 11 | p.init(); 12 | p.network().run(10); 13 | 14 | Assert.assertEquals(4, p.network().allNodes.size()); 15 | Assert.assertEquals(2, p.majority); 16 | 17 | for (Paxos.ProposerNode n : p.proposers) { 18 | Assert.assertTrue(n.seqIP > 0); 19 | } 20 | } 21 | 22 | @Test 23 | public void testCopy() { 24 | Paxos p1 = new Paxos(new Paxos.PaxosParameters(3, 2, 1000, null, null)); 25 | Paxos p2 = p1.copy(); 26 | p1.init(); 27 | p1.network().runMs(2000); 28 | p2.init(); 29 | p2.network().runMs(2000); 30 | 31 | for (Paxos.PaxosNode n1 : p1.network().allNodes) { 32 | Paxos.PaxosNode n2 = p2.network().getNodeById(n1.nodeId); 33 | Assert.assertNotNull(n2); 34 | Assert.assertEquals(n1.getMsgReceived(), n2.getMsgReceived()); 35 | } 36 | } 37 | 38 | @Test 39 | public void testPlay() { 40 | Paxos p1 = new Paxos(new Paxos.PaxosParameters()); 41 | p1.play(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/PingPongTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class PingPongTest { 7 | 8 | @Test 9 | public void testSimple() { 10 | PingPong p = new PingPong(new PingPong.PingPongParameters()); 11 | p.init(); 12 | p.network().run(10); 13 | 14 | Assert.assertEquals(p.params.nodeCt, p.network().allNodes.size()); 15 | for (PingPong.PingPongNode n : p.network().allNodes) { 16 | Assert.assertFalse(n.isDown()); 17 | Assert.assertTrue(n.pong == 0 || n.pong == 1000); 18 | } 19 | } 20 | 21 | /** Test that two runs gives exactly the same result. */ 22 | @Test 23 | public void testCopy() { 24 | PingPong p1 = new PingPong(new PingPong.PingPongParameters()); 25 | PingPong p2 = p1.copy(); 26 | p1.init(); 27 | p1.network().runMs(200); 28 | p2.init(); 29 | p2.network().runMs(200); 30 | 31 | for (PingPong.PingPongNode n1 : p1.network().allNodes) { 32 | PingPong.PingPongNode n2 = p2.network().getNodeById(n1.nodeId); 33 | Assert.assertNotNull(n2); 34 | Assert.assertEquals(n1.pong, n2.pong); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/SanFerminTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | import net.consensys.wittgenstein.core.Node; 7 | import net.consensys.wittgenstein.core.NodeBuilder; 8 | import org.junit.Assert; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | public class SanFerminTest { 13 | private List allNodes; 14 | 15 | @Before 16 | public void setup() { 17 | allNodes = new ArrayList<>(); 18 | NodeBuilder nb = new NodeBuilder(); 19 | int count = 8; 20 | for (int i = 0; i < count; i++) { 21 | allNodes.add(new Node(new Random(0), nb)); 22 | } 23 | } 24 | 25 | @Test 26 | public void testCandidateSet() { 27 | Node n1 = allNodes.get(1); 28 | SanFerminHelper helper = new SanFerminHelper<>(n1, allNodes, new Random(0)); 29 | 30 | List set2 = helper.getCandidateSet(2); 31 | Assert.assertTrue(set2.contains(allNodes.get(0))); 32 | 33 | List set1 = helper.getCandidateSet(1); 34 | Assert.assertTrue(set1.contains(allNodes.get(3))); 35 | Assert.assertFalse(set1.contains(allNodes.get(0))); 36 | 37 | List set0 = helper.getCandidateSet(0); 38 | Assert.assertTrue(set0.contains(allNodes.get(4))); 39 | Assert.assertFalse(set0.contains(allNodes.get(0))); 40 | Assert.assertFalse(set0.contains(allNodes.get(3))); 41 | 42 | // test counter-party set 43 | Node n4 = allNodes.get(4); 44 | SanFerminHelper helper4 = new SanFerminHelper<>(n4, allNodes, new Random(0)); 45 | Assert.assertTrue(helper4.isCandidate(n1, 0)); 46 | } 47 | 48 | @Test 49 | public void testPickNextNodes() { 50 | Node n1 = allNodes.get(1); 51 | SanFerminHelper helper = new SanFerminHelper<>(n1, allNodes, new Random(0)); 52 | 53 | List set2 = helper.pickNextNodes(2, 10); 54 | Assert.assertTrue(set2.contains(allNodes.get(0))); 55 | 56 | List set22 = helper.pickNextNodes(2, 10); 57 | Assert.assertTrue(set22.isEmpty()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/SlushTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.NetworkLatency; 4 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class SlushTest { 9 | private final String nb = 10 | RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 11 | private final String nl = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 12 | 13 | @Test 14 | public void testSimple() { 15 | Slush p = new Slush(new Slush.SlushParameters(100, 7, 7, 4.0 / 7.0, nb, nl)); 16 | p.init(); 17 | p.network().run(10); 18 | 19 | Assert.assertEquals(100, p.network().allNodes.size()); 20 | int uniqueColor = p.network().getNodeById(0).myColor; 21 | for (Slush.SlushNode n : p.network().allNodes) { 22 | Assert.assertEquals(uniqueColor, n.myColor); 23 | } 24 | } 25 | 26 | @Test 27 | public void testCopy() { 28 | Slush p1 = new Slush(new Slush.SlushParameters(60, 5, 7, 4.0 / 7.0, nb, nl)); 29 | Slush p2 = p1.copy(); 30 | p1.init(); 31 | p1.network().runMs(200); 32 | p2.init(); 33 | p2.network().runMs(200); 34 | 35 | for (Slush.SlushNode n1 : p1.network().allNodes) { 36 | Slush.SlushNode n2 = p2.network().getNodeById(n1.nodeId); 37 | Assert.assertNotNull(n2); 38 | Assert.assertEquals(n1.myColor, n2.myColor); 39 | Assert.assertEquals(n1.myQueryNonce, n2.myQueryNonce); 40 | Assert.assertEquals(n1.round, n2.round); 41 | } 42 | } 43 | 44 | @Test 45 | public void testPlay() { 46 | Slush p1 = new Slush(new Slush.SlushParameters(120, 5, 7, 4.0 / 7.0, nb, nl)); 47 | p1.play(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/SnowflakeTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols; 2 | 3 | import net.consensys.wittgenstein.core.NetworkLatency; 4 | import net.consensys.wittgenstein.core.RegistryNodeBuilders; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class SnowflakeTest { 9 | private final String nb = 10 | RegistryNodeBuilders.name(RegistryNodeBuilders.Location.RANDOM, true, 0); 11 | private final String nl = NetworkLatency.NetworkLatencyByDistanceWJitter.class.getSimpleName(); 12 | 13 | @Test 14 | public void testSimple() { 15 | Snowflake p = new Snowflake(new Snowflake.SnowflakeParameters(100, 5, 7, 4.0 / 7.0, 3, nb, nl)); 16 | p.init(); 17 | p.network().run(10); 18 | 19 | Assert.assertEquals(100, p.network().allNodes.size()); 20 | int uniqueColor = p.network().getNodeById(0).myColor; 21 | for (Snowflake.SnowflakeNode n : p.network().allNodes) { 22 | Assert.assertEquals(uniqueColor, n.myColor); 23 | } 24 | } 25 | 26 | @Test 27 | public void testCopy() { 28 | Snowflake p1 = new Snowflake(new Snowflake.SnowflakeParameters(60, 5, 7, 4.0 / 7.0, 3, nb, nl)); 29 | Snowflake p2 = p1.copy(); 30 | p1.init(); 31 | p1.network().runMs(200); 32 | p2.init(); 33 | p2.network().runMs(200); 34 | 35 | for (Snowflake.SnowflakeNode n1 : p1.network().allNodes) { 36 | Snowflake.SnowflakeNode n2 = p2.network().getNodeById(n1.nodeId); 37 | Assert.assertNotNull(n2); 38 | Assert.assertEquals(n1.myColor, n2.myColor); 39 | Assert.assertEquals(n1.myQueryNonce, n2.myQueryNonce); 40 | Assert.assertEquals(n1.cnt, n2.cnt); 41 | } 42 | } 43 | 44 | @Test 45 | public void testPlay() { 46 | Snowflake p1 = 47 | new Snowflake(new Snowflake.SnowflakeParameters(100, 5, 7, 4.0 / 7.0, 3, nb, nl)); 48 | p1.play(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /protocols/src/test/java/net/consensys/wittgenstein/protocols/ethpow/ETHMinerAgentTest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.protocols.ethpow; 2 | 3 | import java.util.Set; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class ETHMinerAgentTest { 8 | 9 | @Test 10 | public void basicTest() { 11 | ETHMinerAgent.ETHPowWithAgent p = ETHMinerAgent.create(.6, 1); 12 | p.init(); 13 | p.network.run(200); 14 | 15 | ETHMinerAgent n = p.getByzNode(); 16 | 17 | Assert.assertTrue(n.head.height > p.genesis.height); 18 | Assert.assertTrue(n.otherMinersHead.height > p.genesis.height); 19 | Assert.assertNotSame(n.otherMinersHead.producer, n); 20 | } 21 | 22 | @Test 23 | public void testSteps() { 24 | ETHMinerAgent.ETHPowWithAgent p = ETHMinerAgent.create(.4, 0); 25 | p.init(); 26 | p.network.runH(2); 27 | 28 | ETHMinerAgent n = p.getByzNode(); 29 | ETHPoW.POWBlock base = n.head; 30 | for (int i = 0; i < 100; i++) { 31 | Assert.assertTrue(n.goNextStep() > 0); 32 | } 33 | Set b = n.blocksReceivedByHeight.get(n.head.height); 34 | for (ETHPoW.POWBlock block : b) { 35 | System.out.println("Block received at height: " + block.height); 36 | System.out.println("Number oof blocks mined: " + (n.head.height - p.genesis.height)); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'core' 2 | include 'protocols' 3 | include 'wserver' 4 | -------------------------------------------------------------------------------- /wserver/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.johnrengelman.shadow" 3 | } 4 | 5 | shadowJar { 6 | archiveName = "wittgenstein-ws.jar" 7 | } 8 | 9 | 10 | dependencies { 11 | implementation project(':core') 12 | implementation project(':protocols') 13 | 14 | implementation "org.springframework:spring-beans:5.1.4.RELEASE" 15 | implementation "org.springframework:spring-context:5.1.4.RELEASE" 16 | 17 | testImplementation "org.springframework.boot:spring-boot-starter-test:2.1.2.RELEASE" 18 | } 19 | 20 | -------------------------------------------------------------------------------- /wserver/src/main/java/net/consensys/wittgenstein/server/ExternalMockImplementation.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.server; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import net.consensys.wittgenstein.core.EnvelopeInfo; 6 | import net.consensys.wittgenstein.core.External; 7 | import net.consensys.wittgenstein.core.Network; 8 | import net.consensys.wittgenstein.core.Node; 9 | import net.consensys.wittgenstein.core.messages.Message; 10 | import net.consensys.wittgenstein.core.messages.SendMessage; 11 | 12 | /** Just print the messages received, but actually relays the execution to a real node. */ 13 | public class ExternalMockImplementation implements External { 14 | private final Network network; 15 | 16 | public ExternalMockImplementation(Network network) { 17 | this.network = network; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return this.getClass().getSimpleName(); 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | @Override 27 | public List receive(EnvelopeInfo ei) { 28 | System.out.println("received:" + ei); 29 | 30 | Network n = (Network) network; 31 | if (network.time != ei.arrivingAt) { 32 | throw new IllegalArgumentException(network.time + " env:" + ei); 33 | } 34 | 35 | TN f = n.getNodeById(ei.from); 36 | TN t = n.getNodeById(ei.to); 37 | Message m = (Message) ei.msg; 38 | m.action(n, f, t); 39 | 40 | return Collections.emptyList(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /wserver/src/main/java/net/consensys/wittgenstein/server/ExternalRest.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.server; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.databind.type.CollectionType; 5 | import java.net.URL; 6 | import java.util.Collections; 7 | import java.util.List; 8 | import net.consensys.wittgenstein.core.EnvelopeInfo; 9 | import net.consensys.wittgenstein.core.External; 10 | import net.consensys.wittgenstein.core.Node; 11 | import net.consensys.wittgenstein.core.messages.SendMessage; 12 | import net.consensys.wittgenstein.server.ws.ObjectMapperFactory; 13 | import org.springframework.boot.web.client.RestTemplateBuilder; 14 | import org.springframework.http.MediaType; 15 | import org.springframework.http.RequestEntity; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.web.client.RestTemplate; 18 | 19 | public class ExternalRest implements External { 20 | private final String httpFullAddress; 21 | private final RestTemplate restTemplate; 22 | private final ObjectMapper objectMapper; 23 | 24 | public ExternalRest(String httpFullAddress) { 25 | this.objectMapper = ObjectMapperFactory.objectMapper(); 26 | this.httpFullAddress = httpFullAddress; 27 | this.restTemplate = new RestTemplateBuilder().rootUri(httpFullAddress).build(); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return this.getClass().getSimpleName() + ": " + httpFullAddress; 33 | } 34 | 35 | @Override 36 | public List receive(EnvelopeInfo ei) { 37 | String jsonString = "na"; 38 | try { 39 | jsonString = objectMapper.writeValueAsString(ei); 40 | 41 | RequestEntity requestEntity = 42 | RequestEntity.put(new URL(httpFullAddress).toURI()) 43 | .contentType(MediaType.APPLICATION_JSON) 44 | .body(jsonString); 45 | 46 | ResponseEntity re = restTemplate.exchange(requestEntity, String.class); 47 | if (re.hasBody()) { 48 | System.out.println("answer: " + re.getBody()); 49 | CollectionType javaType = 50 | objectMapper.getTypeFactory().constructCollectionType(List.class, SendMessage.class); 51 | 52 | return objectMapper.readValue(re.getBody(), javaType); 53 | } 54 | 55 | } catch (Throwable t) { 56 | System.err.println("caught: " + t.getMessage() + ", sending: " + jsonString + ""); 57 | } 58 | return Collections.emptyList(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /wserver/src/main/java/net/consensys/wittgenstein/server/IServer.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.server; 2 | 3 | import java.util.List; 4 | import net.consensys.wittgenstein.core.EnvelopeInfo; 5 | import net.consensys.wittgenstein.core.Node; 6 | import net.consensys.wittgenstein.core.WParameters; 7 | import net.consensys.wittgenstein.core.messages.SendMessage; 8 | 9 | public interface IServer { 10 | 11 | List getNodeInfo(); 12 | 13 | int getTime(); 14 | 15 | void init(String fullClassName, WParameters parameters); 16 | 17 | List getProtocols(); 18 | 19 | WParameters getProtocolParameters(String fullClassName); 20 | 21 | void runMs(int ms); 22 | 23 | Node getNodeInfo(int nodeId); 24 | 25 | List> getMessages(); 26 | 27 | void startNode(int nodeId); 28 | 29 | void stopNode(int nodeId); 30 | 31 | void setExternal(int nodeId, String externalServiceFullAddress); 32 | 33 | void sendMessage(SendMessage msg); 34 | } 35 | -------------------------------------------------------------------------------- /wserver/src/main/java/net/consensys/wittgenstein/server/ws/ExternalWS.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.server.ws; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import net.consensys.wittgenstein.core.EnvelopeInfo; 7 | import net.consensys.wittgenstein.core.External; 8 | import net.consensys.wittgenstein.core.Node; 9 | import net.consensys.wittgenstein.core.messages.SendMessage; 10 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Primary; 13 | import org.springframework.web.bind.annotation.PutMapping; 14 | import org.springframework.web.bind.annotation.RequestBody; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | /** Dummy implementation of an external node. It just prints the messages. */ 19 | @RestController 20 | @EnableAutoConfiguration 21 | @RequestMapping("/w") 22 | public class ExternalWS implements External { 23 | 24 | @PutMapping(value = "/external_sink") 25 | @Override 26 | public List receive(@RequestBody EnvelopeInfo ei) { 27 | System.out.println("Received message: " + ei); 28 | 29 | // FloodMessage f = new FloodMessage<>(2000, 10,10); 30 | // SendMessage m = new SendMessage(0, new ArrayList<>( List.of(1,2,3)), 1200, 1, f); 31 | // return new ArrayList<>(Collections.singleton(m)); 32 | return Collections.emptyList(); 33 | } 34 | 35 | @Bean 36 | @Primary 37 | public ObjectMapper objectMapper() { 38 | return ObjectMapperFactory.objectMapper(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /wserver/src/main/java/net/consensys/wittgenstein/server/ws/ObjectMapperFactory.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.server.ws; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.MapperFeature; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import com.fasterxml.jackson.databind.jsontype.NamedType; 9 | import net.consensys.wittgenstein.core.utils.Reflects; 10 | import net.consensys.wittgenstein.server.Server; 11 | 12 | public class ObjectMapperFactory { 13 | 14 | @SuppressWarnings("unused") 15 | public static ObjectMapper objectMapper() { 16 | ObjectMapper mapper = new ObjectMapper(); 17 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 18 | mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true); 19 | 20 | mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 21 | mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); 22 | 23 | mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 24 | 25 | for (String s : Server.getMessageType()) { 26 | Class c = Reflects.forName(s); 27 | mapper.registerSubtypes(new NamedType(c, c.getSimpleName())); 28 | } 29 | 30 | return mapper; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /wserver/src/main/java/net/consensys/wittgenstein/server/ws/WServer.java: -------------------------------------------------------------------------------- 1 | package net.consensys.wittgenstein.server.ws; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.databind.jsontype.NamedType; 5 | import java.util.List; 6 | import net.consensys.wittgenstein.core.EnvelopeInfo; 7 | import net.consensys.wittgenstein.core.External; 8 | import net.consensys.wittgenstein.core.Node; 9 | import net.consensys.wittgenstein.core.WParameters; 10 | import net.consensys.wittgenstein.core.messages.SendMessage; 11 | import net.consensys.wittgenstein.server.*; 12 | import org.springframework.boot.SpringApplication; 13 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Primary; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | @RestController 19 | @EnableAutoConfiguration 20 | @RequestMapping("/w") 21 | public class WServer extends ExternalWS implements IServer, External { 22 | private Server server = new Server(); 23 | 24 | @GetMapping(value = "/network/nodes") 25 | @Override 26 | public List getNodeInfo() { 27 | return server.getNodeInfo(); 28 | } 29 | 30 | @GetMapping(value = "/network/time") 31 | @Override 32 | public int getTime() { 33 | return server.getTime(); 34 | } 35 | 36 | @GetMapping(value = "/protocols") 37 | @Override 38 | public List getProtocols() { 39 | return server.getProtocols(); 40 | } 41 | 42 | @GetMapping(value = "/protocols/{fullClassName}") 43 | @Override 44 | public WParameters getProtocolParameters(@PathVariable("fullClassName") String fullClassName) { 45 | return server.getProtocolParameters(fullClassName); 46 | } 47 | 48 | @PostMapping(value = "/network/init/{fullClassName}") 49 | public void init( 50 | @PathVariable("fullClassName") String fullClassName, @RequestBody WParameters parameters) { 51 | server.init(fullClassName, parameters); 52 | } 53 | 54 | @PostMapping(value = "/network/runMs/{ms}") 55 | @Override 56 | public void runMs(@PathVariable("ms") int ms) { 57 | server.runMs(ms); 58 | } 59 | 60 | @GetMapping(value = "/network/nodes/{nodeId}") 61 | @Override 62 | public Node getNodeInfo(@PathVariable("nodeId") int nodeId) { 63 | return server.getNodeInfo(nodeId); 64 | } 65 | 66 | @GetMapping(value = "/network/messages") 67 | @Override 68 | public List> getMessages() { 69 | return server.getMessages(); 70 | } 71 | 72 | @PostMapping(value = "/nodes/{nodeId}/start") 73 | @Override 74 | public void startNode(@PathVariable("nodeId") int nodeId) { 75 | server.startNode(nodeId); 76 | } 77 | 78 | @PostMapping(value = "/network/nodes/{nodeId}/stop") 79 | @Override 80 | public void stopNode(@PathVariable("nodeId") int nodeId) { 81 | server.stopNode(nodeId); 82 | } 83 | 84 | @PostMapping(value = "/network/nodes/{nodeId}/external") 85 | @Override 86 | public void setExternal( 87 | @PathVariable("nodeId") int nodeId, @RequestBody String externalServiceFullAddress) { 88 | server.setExternal(nodeId, externalServiceFullAddress); 89 | } 90 | 91 | @PostMapping(value = "/network/send") 92 | @Override 93 | public void sendMessage(@RequestBody SendMessage msg) { 94 | server.sendMessage(msg); 95 | } 96 | 97 | /** We map all fields in the parameters, not taking into account the getters/setters */ 98 | @SuppressWarnings("unused") 99 | @Bean 100 | @Primary 101 | public ObjectMapper objectMapper() { 102 | ObjectMapper mapper = ObjectMapperFactory.objectMapper(); 103 | 104 | for (Class p : server.getParametersName()) { 105 | mapper.registerSubtypes(new NamedType(p, p.getSimpleName())); 106 | } 107 | 108 | return mapper; 109 | } 110 | 111 | public static void main(String... args) { 112 | SpringApplication.run(WServer.class, args); 113 | } 114 | } 115 | --------------------------------------------------------------------------------