From 51d06e8cfd3163fe1b5d435521a928e04b8f1828 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Fri, 14 Oct 2022 00:00:46 -0400 Subject: [PATCH 01/34] Replace tendermint with celestiaorg tendermint version --- go.mod | 11 +++++------ go.sum | 32 ++++++++++++++++---------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index d9f756183bc..0f3c2318c88 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/tendermint/tm-db v0.6.7 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e - google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959 + google.golang.org/genproto v0.0.0-20221013201013-33fc6f83cba4 google.golang.org/grpc v1.50.0 google.golang.org/protobuf v1.28.1 pgregory.net/rapid v0.4.7 @@ -229,12 +229,12 @@ require ( go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.23.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect + golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458 // indirect golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 // indirect golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect + golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/text v0.3.8 // indirect golang.org/x/tools v0.1.12 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/api v0.93.0 // indirect @@ -256,8 +256,7 @@ replace ( // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 - - github.com/jhump/protoreflect => github.com/jhump/protoreflect v1.9.0 + github.com/tendermint/tendermint => github.com/celestiaorg/tendermint v0.34.18-0.20221013213714-8be9b54c8c21 ) retract v0.46.2 diff --git a/go.sum b/go.sum index d2fc92f7ab2..1e02b21db30 100644 --- a/go.sum +++ b/go.sum @@ -194,6 +194,8 @@ github.com/celestiaorg/rollmint v0.4.0 h1:+eu2m25ALhlHj9pbAElzq2pSg6WJ4Bn8/WqL2H github.com/celestiaorg/rollmint v0.4.0/go.mod h1:EBnqxgmcH0MIgNgiI8NyVa1sNSzCyCpvAlAHd9myhk4= github.com/celestiaorg/smt v0.3.0 h1:Hc6m8fIVRajrg/Saf8ivX4xw551LHzOs8kqeadd6h9s= github.com/celestiaorg/smt v0.3.0/go.mod h1:/sdYDakowo/XaxS2Fl7CBqtuf/O2uTqF2zmAUFAtAiw= +github.com/celestiaorg/tendermint v0.34.18-0.20221013213714-8be9b54c8c21 h1:eRc/DaqHOw35b4Kf2Fqu1frD/88TD3enIcbzWJu2MIg= +github.com/celestiaorg/tendermint v0.34.18-0.20221013213714-8be9b54c8c21/go.mod h1:zoyyiiihvTW8DnOr63YLxhYn/WK/QmE74CeIpS++hBE= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= @@ -566,7 +568,6 @@ github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= -github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= @@ -715,8 +716,12 @@ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1C github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= -github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= +github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= +github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= +github.com/jhump/protoreflect v1.12.1-0.20220721211354-060cc04fc18b h1:izTof8BKh/nE1wrKOrloNA5q4odOarjf+Xpe+4qow98= +github.com/jhump/protoreflect v1.12.1-0.20220721211354-060cc04fc18b/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -1000,7 +1005,6 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= -github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -1248,8 +1252,6 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RM github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/tendermint v0.34.22 h1:XMhtC8s8QqJO4l/dn+TkQvevTRSow3Vixjclr41o+2Q= -github.com/tendermint/tendermint v0.34.22/go.mod h1:YpP5vBEAKUT4g6oyfjKgFeZmdB/GjkJAxfF+cgmJg6Y= github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -1490,8 +1492,8 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458 h1:MgJ6t2zo8v0tbmLCueaCbF1RM+TtB0rs3Lv8DGtOIpY= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1645,8 +1647,8 @@ golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1660,8 +1662,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1714,10 +1717,8 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1900,8 +1901,8 @@ google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljW google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959 h1:hw4Y42zL1VyVKxPgRHHh191fpVBGV8sNVmcow5Z8VXY= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20221013201013-33fc6f83cba4 h1:nZ28yoLJWNLTcERW43BN+JDsNQOdiZOFB9Dly/IUrjw= +google.golang.org/genproto v0.0.0-20221013201013-33fc6f83cba4/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -1955,7 +1956,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= From 9396aabcb0f4d1f09116c47aae0f8b67e1c6a668 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Fri, 14 Oct 2022 00:01:06 -0400 Subject: [PATCH 02/34] Add new ABCI method skeleton: build works now --- baseapp/abci.go | 19 +++++++++++++++++++ store/rootmulti/store.go | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/baseapp/abci.go b/baseapp/abci.go index 74a5b04f51b..ac1f09eae8d 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -19,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" + "github.com/cosmos/cosmos-sdk/store/rootmulti" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -131,6 +132,24 @@ func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { } } +func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGetAppHash) { + cms := app.cms.(*rootmulti.Store) + + appHash := cms.GetAppHash() + res = abci.ResponseGetAppHash{ + AppHash: appHash, + } + return res +} + +func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res abci.ResponseGenerateFraudProof) { + return abci.ResponseGenerateFraudProof{} +} + +func (app *BaseApp) VerifyFraudProof(req abci.RequestVerifyFraudProof) (res abci.ResponseVerifyFraudProof) { + return abci.ResponseVerifyFraudProof{} +} + // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { if app.cms.TracingEnabled() { diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index b6a7eeab4ab..690b5e1f0eb 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -984,6 +984,10 @@ func (rs *Store) flushMetadata(db dbm.DB, version int64, cInfo *types.CommitInfo rs.logger.Debug("flushing metadata finished", "height", version) } +func (s *Store) GetAppHash() []byte { + return s.LastCommitID().Hash +} + type storeParams struct { key types.StoreKey db dbm.DB From 1975e89ebab8b75ed738fc14beb0513030a31709 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Fri, 14 Oct 2022 00:18:52 -0400 Subject: [PATCH 03/34] Add fraudproof data structure --- baseapp/fraudproof.go | 201 ++++++++++++++++++++++++++++++++++++ store/v2alpha1/smt/proof.go | 4 + 2 files changed, 205 insertions(+) create mode 100644 baseapp/fraudproof.go diff --git a/baseapp/fraudproof.go b/baseapp/fraudproof.go new file mode 100644 index 00000000000..ef4e50b18a5 --- /dev/null +++ b/baseapp/fraudproof.go @@ -0,0 +1,201 @@ +package baseapp + +import ( + "bytes" + "crypto/sha256" + "fmt" + + "github.com/cosmos/cosmos-sdk/store/mem" + "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" + smtlib "github.com/lazyledger/smt" + abci "github.com/tendermint/tendermint/abci/types" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" +) + +// Represents a single-round fraudProof +type FraudProof struct { + // The block height to load state of + blockHeight int64 + + // TODO: Add Proof that appHash is inside merklized ISRs in block header at block height + + appHash []byte + // A map from module name to state witness + stateWitness map[string]StateWitness + + // Fraudulent state transition has to be one of these + // Only one have of these three can be non-nil + fraudulentBeginBlock *abci.RequestBeginBlock + fraudulentDeliverTx *abci.RequestDeliverTx + fraudulentEndBlock *abci.RequestEndBlock + + // TODO: Add Proof that fraudulent state transition is inside merkelizied transactions in block header +} + +// State witness with a list of all witness data +type StateWitness struct { + // store level proof + Proof tmcrypto.ProofOp + RootHash []byte + // List of witness data + WitnessData []WitnessData +} + +// Witness data containing a key/value pair and a SMT proof for said key/value pair +type WitnessData struct { + Key []byte + Value []byte + Proof tmcrypto.ProofOp +} + +func (fraudProof *FraudProof) getModules() []string { + keys := make([]string, 0, len(fraudProof.stateWitness)) + for k := range fraudProof.stateWitness { + keys = append(keys, k) + } + return keys +} + +func (fraudProof *FraudProof) getSubstoreSMTs() (map[string]*smtlib.SparseMerkleTree, error) { + storeKeyToSMT := make(map[string]*smtlib.SparseMerkleTree) + for storeKey, stateWitness := range fraudProof.stateWitness { + rootHash := stateWitness.RootHash + substoreDeepSMT := smtlib.NewDeepSparseMerkleSubTree(smtlib.NewSimpleMap(), smtlib.NewSimpleMap(), sha256.New(), rootHash) + for _, witnessData := range stateWitness.WitnessData { + proofOp, key, val := witnessData.Proof, witnessData.Key, witnessData.Value + proof, err := smt.ProofDecoder(proofOp) + if err != nil { + return nil, err + } + smtProof := proof.(*smt.ProofOp).GetProof() + substoreDeepSMT.AddBranch(smtProof, key, val) + } + storeKeyToSMT[storeKey] = substoreDeepSMT.SparseMerkleTree + } + return storeKeyToSMT, nil +} + +func (fraudProof *FraudProof) extractStore() map[string]types.KVStore { + store := make(map[string]types.KVStore) + for storeKey, stateWitness := range fraudProof.stateWitness { + subStore := mem.NewStore() + for _, witnessData := range stateWitness.WitnessData { + key, val := witnessData.Key, witnessData.Value + subStore.Set(key, val) + } + store[storeKey] = subStore + } + return store +} + +// Returns true only if only one of the three pointers is nil +func (fraudProof *FraudProof) checkFraudulentStateTransition() bool { + if fraudProof.fraudulentBeginBlock != nil { + return fraudProof.fraudulentDeliverTx == nil && fraudProof.fraudulentEndBlock == nil + } + if fraudProof.fraudulentDeliverTx != nil { + return fraudProof.fraudulentEndBlock == nil + } + return fraudProof.fraudulentEndBlock != nil +} + +func (fraudProof *FraudProof) verifyFraudProof() (bool, error) { + if !fraudProof.checkFraudulentStateTransition() { + return false, fmt.Errorf("fraudProof has more than one type of fradulent state transitions marked nil") + } + for storeKey, stateWitness := range fraudProof.stateWitness { + proofOp := stateWitness.Proof + proof, err := types.CommitmentOpDecoder(proofOp) + if err != nil { + return false, err + } + if !bytes.Equal(proof.GetKey(), []byte(storeKey)) { + return false, fmt.Errorf("got storeKey: %s, expected: %s", string(proof.GetKey()), storeKey) + } + appHash, err := proof.Run([][]byte{stateWitness.RootHash}) + if err != nil { + return false, err + } + if !bytes.Equal(appHash[0], fraudProof.appHash) { + return false, fmt.Errorf("got appHash: %s, expected: %s", string(fraudProof.appHash), string(fraudProof.appHash)) + } + // Fraudproof verification on a substore level + for _, witness := range stateWitness.WitnessData { + proofOp, key, value := witness.Proof, witness.Key, witness.Value + if err != nil { + return false, err + } + if !bytes.Equal(key, proofOp.GetKey()) { + return false, fmt.Errorf("got key: %s, expected: %s for storeKey: %s", string(key), string(proof.GetKey()), storeKey) + } + proof, err := smt.ProofDecoder(proofOp) + if err != nil { + return false, err + } + rootHash, err := proof.Run([][]byte{value}) + if err != nil { + return false, err + } + if !bytes.Equal(rootHash[0], stateWitness.RootHash) { + return false, fmt.Errorf("got rootHash: %s, expected: %s for storeKey: %s", string(rootHash[0]), string(stateWitness.RootHash), storeKey) + } + } + } + return true, nil +} + +func (fraudProof *FraudProof) toABCI() abci.FraudProof { + abciStateWitness := make(map[string]*abci.StateWitness) + for storeKey, stateWitness := range fraudProof.stateWitness { + abciWitnessData := make([]*abci.WitnessData, 0, len(stateWitness.WitnessData)) + for _, witnessData := range stateWitness.WitnessData { + abciWitness := abci.WitnessData{ + Key: witnessData.Key, + Value: witnessData.Value, + Proof: &witnessData.Proof, + } + abciWitnessData = append(abciWitnessData, &abciWitness) + } + proof := stateWitness.Proof + abciStateWitness[storeKey] = &abci.StateWitness{ + Proof: &proof, + RootHash: stateWitness.RootHash, + WitnessData: abciWitnessData, + } + } + return abci.FraudProof{ + BlockHeight: fraudProof.blockHeight, + AppHash: fraudProof.appHash, + StateWitness: abciStateWitness, + FraudulentBeginBlock: fraudProof.fraudulentBeginBlock, + FraudulentDeliverTx: fraudProof.fraudulentDeliverTx, + FraudulentEndBlock: fraudProof.fraudulentEndBlock, + } +} + +func (fraudProof *FraudProof) fromABCI(abciFraudProof abci.FraudProof) { + stateWitness := make(map[string]StateWitness) + for storeKey, abciStateWitness := range abciFraudProof.StateWitness { + witnessData := make([]WitnessData, 0, len(abciStateWitness.WitnessData)) + for _, abciWitnessData := range abciStateWitness.WitnessData { + witness := WitnessData{ + Key: abciWitnessData.Key, + Value: abciWitnessData.Value, + Proof: *abciWitnessData.Proof, + } + witnessData = append(witnessData, witness) + } + stateWitness[storeKey] = StateWitness{ + Proof: *abciStateWitness.Proof, + RootHash: abciStateWitness.RootHash, + WitnessData: witnessData, + } + } + fraudProof.blockHeight = abciFraudProof.BlockHeight + fraudProof.appHash = abciFraudProof.AppHash + fraudProof.stateWitness = stateWitness + fraudProof.fraudulentBeginBlock = abciFraudProof.FraudulentBeginBlock + fraudProof.fraudulentDeliverTx = abciFraudProof.FraudulentDeliverTx + fraudProof.fraudulentEndBlock = abciFraudProof.FraudulentEndBlock +} diff --git a/store/v2alpha1/smt/proof.go b/store/v2alpha1/smt/proof.go index d247f1bf661..32433d5085d 100644 --- a/store/v2alpha1/smt/proof.go +++ b/store/v2alpha1/smt/proof.go @@ -62,6 +62,10 @@ func (p *ProofOp) GetKey() []byte { return p.Key } +func (p *ProofOp) GetProof() smt.SparseMerkleProof { + return p.Proof +} + func (p *ProofOp) ProofOp() tmmerkle.ProofOp { var data bytes.Buffer enc := gob.NewEncoder(&data) From 8168e32a745a38f70811aecb510ca2c1023c47cc Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Fri, 14 Oct 2022 00:38:18 -0400 Subject: [PATCH 04/34] Add helper methods to baseapp test for fraudproofs --- baseapp/baseapp_test.go | 230 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index bf5d6b213e8..da56c1dec82 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -26,6 +26,7 @@ import ( snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store/rootmulti" storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" @@ -34,8 +35,9 @@ import ( ) var ( - capKey1 = sdk.NewKVStoreKey("key1") - capKey2 = sdk.NewKVStoreKey("key2") + capKey1 = sdk.NewKVStoreKey("key1") + capKey2 = sdk.NewKVStoreKey("key2") + randSource = int64(123456789) // testTxPriority is the CheckTx priority that we set in the test // antehandler. @@ -2173,3 +2175,227 @@ func TestBaseApp_EndBlock(t *testing.T) { require.Equal(t, int64(100), res.GetValidatorUpdates()[0].Power) require.Equal(t, cp.Block.MaxGas, res.ConsensusParamUpdates.Block.MaxGas) } + +func executeBlockWithArbitraryTxs(t *testing.T, app *BaseApp, numTransactions int, blockHeight int64) []txTest { + codec := codec.NewLegacyAmino() + registerTestCodec(codec) + r := rand.New(rand.NewSource(randSource)) + randSource += 1 + keyCounter := r.Intn(10000) + txs := make([]txTest, 0) + + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: blockHeight}}) + for txNum := 0; txNum < numTransactions; txNum++ { + tx := txTest{Msgs: []sdk.Msg{}} + for msgNum := 0; msgNum < 1; msgNum++ { + key := []byte(fmt.Sprintf("%v", keyCounter)) + value := make([]byte, 10000) + _, err := r.Read(value) + require.NoError(t, err) + tx.Msgs = append(tx.Msgs, msgKeyValue{Key: key, Value: value}) + keyCounter++ + } + txBytes, err := codec.Marshal(tx) + require.NoError(t, err) + resp := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.True(t, resp.IsOK(), "%v", resp.String()) + txs = append(txs, tx) + } + app.EndBlock(abci.RequestEndBlock{Height: blockHeight}) + return txs +} + +func getBlockWithArbitraryTxs(t *testing.T, app *BaseApp, numTransactions int, blockHeight int64) (*abci.RequestBeginBlock, []txTest, []*abci.RequestDeliverTx, *abci.RequestEndBlock) { + codec := codec.NewLegacyAmino() + registerTestCodec(codec) + r := rand.New(rand.NewSource(randSource)) + randSource += 1 + keyCounter := r.Intn(10000) + txs := make([]txTest, 0) + + beginRequest := abci.RequestBeginBlock{Header: tmproto.Header{Height: blockHeight}} + deliverRequests := make([]*abci.RequestDeliverTx, 0) + for txNum := 0; txNum < numTransactions; txNum++ { + tx := txTest{Msgs: []sdk.Msg{}} + for msgNum := 0; msgNum < 1; msgNum++ { + key := []byte(fmt.Sprintf("%v", keyCounter)) + value := make([]byte, 10000) + _, err := r.Read(value) + require.NoError(t, err) + tx.Msgs = append(tx.Msgs, msgKeyValue{Key: key, Value: value}) + keyCounter++ + } + txBytes, err := codec.Marshal(tx) + require.NoError(t, err) + deliverRequest := abci.RequestDeliverTx{Tx: txBytes} + deliverRequests = append(deliverRequests, &deliverRequest) + txs = append(txs, tx) + } + endBlockRequest := abci.RequestEndBlock{Height: blockHeight} + return &beginRequest, txs, deliverRequests, &endBlockRequest +} + +func executeBlock(t *testing.T, app *BaseApp, txs []txTest, blockHeight int64) { + codec := codec.NewLegacyAmino() + registerTestCodec(codec) + + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: blockHeight}}) + for _, tx := range txs { + txBytes, err := codec.Marshal(tx) + require.NoError(t, err) + resp := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.True(t, resp.IsOK(), "%v", resp.String()) + } + app.EndBlock(abci.RequestEndBlock{Height: blockHeight}) +} + +func executeBlockWithRequests(t *testing.T, app *BaseApp, beginRequest *abci.RequestBeginBlock, deliverRequests []*abci.RequestDeliverTx, endRequest *abci.RequestEndBlock, blockHeight int64) { + if beginRequest != nil { + if blockHeight != 0 { + beginRequest.Header.Height = blockHeight + } + app.BeginBlock(*beginRequest) + } + for _, deliverRequest := range deliverRequests { + require.NotNil(t, deliverRequest) + resp := app.DeliverTx(*deliverRequest) + require.True(t, resp.IsOK(), "%v", resp.String()) + } + if endRequest != nil { + app.EndBlock(*endRequest) + } +} + +// Takes the key embedded in given message, and returns a +// deliverRequest with a tx that has the same key but a different value +func getFraudTx(t *testing.T, tx txTest) *abci.RequestDeliverTx { + msgs := tx.GetMsgs() + require.NotEmpty(t, msgs) + msgKV := msgs[0].(msgKeyValue) + key := msgKV.Key + codec := codec.NewLegacyAmino() + registerTestCodec(codec) + + fraudTx := txTest{Msgs: []sdk.Msg{}} + r := rand.New(rand.NewSource(randSource)) + randSource += 1 + + newValue := make([]byte, 10000) + _, err := r.Read(newValue) + require.NoError(t, err) + fraudTx.Msgs = append(fraudTx.Msgs, msgKeyValue{Key: key, Value: newValue}) + + fraudTxBytes, err := codec.Marshal(fraudTx) + require.Nil(t, err) + fraudDeliverRequest := abci.RequestDeliverTx{Tx: fraudTxBytes} + + return &fraudDeliverRequest +} + +func TestGenerateAndLoadFraudProof(t *testing.T) { + /* + Tests switch between a baseapp and fraudproof and covers parts of the fraudproof cycle. Steps: + 1. Initialize a baseapp, B1, with some state, S0 + 2. Make some state transition to state S1 by doing some transactions, commit those transactions, and save + resulting SMT root hash + 3. Make another set of state transitions to state S2 but do not commit + 4. Generate a fraudproof which should ignore the uncommitted set of transactions, and export S1 into a fraudProof data structure (minimal snapshot) + 5. Verify the fraudproof and check verification passes (done in light client) + 6. Load a fresh baseapp, B2, with the contents of fraud proof data structure from (4) so it can begin from state S1. + 7. Check if the SMT root hashes of the new app with saved SMT root hash + + Tests to write in future: + + 1. Block with bad txs: Txs that exceed gas limits, validateBasic fails, unregistered messages (see TestRunInvalidTransaction) + 2. Block with invalid appHash at the end + 3. Corrupted Fraud Proof: bad SMT format, insufficient key-value pairs inside SMT needed to verify fraud + 4. Bad block, fraud proof needed, fraud proof works, chain halts (happy case) + */ + + storeTraceBuf := &bytes.Buffer{} + subStoreTraceBuf := &bytes.Buffer{} + + routerOpt := func(bapp *BaseApp) { + bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + kv := msg.(*msgKeyValue) + bapp.cms.GetKVStore(capKey2).Set(kv.Key, kv.Value) + return &sdk.Result{}, nil + })) + } + + // BaseApp, B1 + appB1 := setupBaseApp(t, + routerOpt, + SetSubstoreTracer(storeTraceBuf), + SetTracerFor(capKey2.Name(), subStoreTraceBuf), + ) + + // B1 <- S0 + appB1.InitChain(abci.RequestInitChain{}) + + numTransactions := 2 + // B1 <- S1 + executeBlockWithArbitraryTxs(t, appB1, numTransactions, 1) + appB1.Commit() + + // B1 <- S2 + beginRequest, txs, deliverRequests, _ := getBlockWithArbitraryTxs(t, appB1, numTransactions, 2) + + // Modify deliverRequests to discard last tx in the block + nonFraudulentDeliverRequests := deliverRequests[0 : len(deliverRequests)-1] + txs = txs[0 : len(txs)-1] + require.NotEmpty(t, txs) + fraudDeliverRequest := getFraudTx(t, txs[0]) + + executeBlockWithRequests(t, appB1, beginRequest, nonFraudulentDeliverRequests, nil, 0) + + // Save appHash, substoreHash here for comparision later + appHashB1, err := appB1.cms.(*multi.Store).GetAppHash() + require.Nil(t, err) + storeHashB1 := appB1.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() + + routerOpts := make(map[string]AppOptionFunc) + newRouterOpt := func(bapp *BaseApp) { + bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + kv := msg.(*msgKeyValue) + cms := bapp.cms.(*multi.Store) + sKeys := cms.GetStoreKeys() + largestKey := sKeys[0] + for _, sKey := range sKeys[1:] { + if sKey.Name() > largestKey.Name() { + largestKey = sKey + } + } + bapp.cms.GetKVStore(largestKey).Set(kv.Key, kv.Value) + return &sdk.Result{}, nil + })) + } + routerOpts[capKey2.Name()] = newRouterOpt + + resp := appB1.generateFraudProofWithRouterOpts( + abci.RequestGenerateFraudProof{ + BeginBlockRequest: *beginRequest, DeliverTxRequests: append(nonFraudulentDeliverRequests, fraudDeliverRequest), EndBlockRequest: nil, + }, + routerOpts, + ) + + // Light Client + fraudProof := FraudProof{} + fraudProof.fromABCI(*resp.FraudProof) + require.Equal(t, appHashB1, fraudProof.appHash) + fraudProofVerified, err := fraudProof.verifyFraudProof() + require.Nil(t, err) + require.True(t, fraudProofVerified) + + // Now we take contents of the fraud proof which was recorded with S2 and try to populate a fresh baseapp B2 with it + // B2 <- S2 + codec := codec.NewLegacyAmino() + registerTestCodec(codec) + appB2, err := SetupBaseAppFromFraudProof(t.Name(), defaultLogger(), dbm.NewMemDB(), testTxDecoder(codec), fraudProof, AppOptionFunc(routerOpt)) + require.Nil(t, err) + appB2Hash, err := appB2.cms.(*multi.Store).GetAppHash() + require.Nil(t, err) + require.Equal(t, appHashB1, appB2Hash) + storeHashB2 := appB2.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() + require.Equal(t, storeHashB1, storeHashB2) +} From 20e50ff8aba279ddea8a65eee14c807ed987e2ac Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Fri, 14 Oct 2022 01:02:33 -0400 Subject: [PATCH 05/34] Add TODOs in baseapp test method --- baseapp/abci.go | 5 +++++ baseapp/baseapp_test.go | 21 ++++++++++++--------- store/rootmulti/store.go | 8 ++++++++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index ac1f09eae8d..0d163113336 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -146,6 +146,11 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res return abci.ResponseGenerateFraudProof{} } +func (app *BaseApp) generateFraudProofWithRouterOpts(req abci.RequestGenerateFraudProof, routerOpts map[string]func(*BaseApp)) (res abci.ResponseGenerateFraudProof) { + // app.SetRouterOpts(routerOpts) + return app.GenerateFraudProof(req) +} + func (app *BaseApp) VerifyFraudProof(req abci.RequestVerifyFraudProof) (res abci.ResponseVerifyFraudProof) { return abci.ResponseVerifyFraudProof{} } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index da56c1dec82..0d6d6b4d22b 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -2326,8 +2326,9 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { // BaseApp, B1 appB1 := setupBaseApp(t, routerOpt, - SetSubstoreTracer(storeTraceBuf), - SetTracerFor(capKey2.Name(), subStoreTraceBuf), + // TODO: Write IAVL equivalent options somehow + // SetSubstoreTracer(storeTraceBuf), + // SetTracerFor(capKey2.Name(), subStoreTraceBuf), ) // B1 <- S0 @@ -2350,15 +2351,16 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { executeBlockWithRequests(t, appB1, beginRequest, nonFraudulentDeliverRequests, nil, 0) // Save appHash, substoreHash here for comparision later - appHashB1, err := appB1.cms.(*multi.Store).GetAppHash() - require.Nil(t, err) - storeHashB1 := appB1.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() + appHashB1 := appB1.cms.(*rootmulti.Store).GetAppHash() + + //TODO: Write iavl equivalent somehow + // storeHashB1 := appB1.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() - routerOpts := make(map[string]AppOptionFunc) + routerOpts := make(map[string]func(*BaseApp)) newRouterOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { kv := msg.(*msgKeyValue) - cms := bapp.cms.(*multi.Store) + cms := bapp.cms.(*rootmulti.Store) sKeys := cms.GetStoreKeys() largestKey := sKeys[0] for _, sKey := range sKeys[1:] { @@ -2372,6 +2374,7 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { } routerOpts[capKey2.Name()] = newRouterOpt + // THINK: No routerOpts in this baseapp version, how do I bypass this requirement? resp := appB1.generateFraudProofWithRouterOpts( abci.RequestGenerateFraudProof{ BeginBlockRequest: *beginRequest, DeliverTxRequests: append(nonFraudulentDeliverRequests, fraudDeliverRequest), EndBlockRequest: nil, @@ -2396,6 +2399,6 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { appB2Hash, err := appB2.cms.(*multi.Store).GetAppHash() require.Nil(t, err) require.Equal(t, appHashB1, appB2Hash) - storeHashB2 := appB2.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() - require.Equal(t, storeHashB1, storeHashB2) + // storeHashB2 := appB2.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() + // require.Equal(t, storeHashB1, storeHashB2) } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 690b5e1f0eb..2dc559843fc 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -163,6 +163,14 @@ func (rs *Store) GetCommitKVStore(key types.StoreKey) types.CommitKVStore { return rs.stores[key] } +func (s *Store) GetStoreKeys() []types.StoreKey { + storeKeys := make([]types.StoreKey, 0, len(s.keysByName)) + for _, sk := range s.keysByName { + storeKeys = append(storeKeys, sk) + } + return storeKeys +} + // StoreKeysByName returns mapping storeNames -> StoreKeys func (rs *Store) StoreKeysByName() map[string]types.StoreKey { return rs.keysByName From 9801e3e671bff5f7bde93d0801060dd4f4017413 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Fri, 14 Oct 2022 01:13:49 -0400 Subject: [PATCH 06/34] Add SetupBaseParams with SMT, WIP to IAVL --- baseapp/baseapp.go | 44 +++++++++++++++++++++++++++++++++++++++++ baseapp/baseapp_test.go | 6 ++---- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 448ef792fd0..04c01c61239 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/gogo/protobuf/proto" + smtlib "github.com/lazyledger/smt" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/log" @@ -838,3 +839,46 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) { return proto.Marshal(&sdk.TxMsgData{MsgResponses: msgResponses}) } + +// set up a new baseapp from given params +func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToSMT map[string]*smtlib.SparseMerkleTree, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { + storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) + storeKeyToSubstoreHash := make(map[string][]byte) + for _, storeKeyName := range storeKeyNames { + storeKey := sdk.NewKVStoreKey(storeKeyName) + storeKeys = append(storeKeys, storeKey) + subStore := storeToLoadFrom[storeKeyName] + it := subStore.Iterator(nil, nil) + substoreSMT := storeKeyToSMT[storeKeyName] + for ; it.Valid(); it.Next() { + key, val := it.Key(), it.Value() + proof, err := substoreSMT.Prove(key) + if err != nil { + return nil, err + } + options = append(options, SetDeepSMTBranchKVPair(storeKey, substoreSMT.Root(), proof, key, val)) + } + storeKeyToSubstoreHash[storeKeyName] = substoreSMT.Root() + } + + options = append(options, SetSubstoresWithRoots(storeKeyToSubstoreHash, storeKeys...)) + + // This initial height is used in `BeginBlock` in `validateHeight` + options = append(options, SetInitialHeight(blockHeight)) + + // make list of options to pass by parsing fraudproof + app := NewBaseApp(appName, logger, db, txDecoder, options...) + // stores are mounted + err := app.Init() + + return app, err +} + +// set up a new baseapp from a fraudproof +func SetupBaseAppFromFraudProof(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, fraudProof FraudProof, options ...func(*BaseApp)) (*BaseApp, error) { + storeKeyToSMT, err := fraudProof.getSubstoreSMTs() + if err != nil { + return nil, err + } + return SetupBaseAppFromParams(appName, logger, db, txDecoder, fraudProof.getModules(), storeKeyToSMT, fraudProof.blockHeight, fraudProof.extractStore(), options...) +} diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 0d6d6b4d22b..3234ec56109 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -26,7 +26,6 @@ import ( snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store/rootmulti" storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/multi" "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" @@ -2394,10 +2393,9 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { // B2 <- S2 codec := codec.NewLegacyAmino() registerTestCodec(codec) - appB2, err := SetupBaseAppFromFraudProof(t.Name(), defaultLogger(), dbm.NewMemDB(), testTxDecoder(codec), fraudProof, AppOptionFunc(routerOpt)) - require.Nil(t, err) - appB2Hash, err := appB2.cms.(*multi.Store).GetAppHash() + appB2, err := SetupBaseAppFromFraudProof(t.Name(), defaultLogger(), dbm.NewMemDB(), testTxDecoder(codec), fraudProof, routerOpt) require.Nil(t, err) + appB2Hash := appB2.cms.(*rootmulti.Store).GetAppHash() require.Equal(t, appHashB1, appB2Hash) // storeHashB2 := appB2.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() // require.Equal(t, storeHashB1, storeHashB2) From b3737ae9aaab1e3ac551b9df1e462301f639c52c Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 11:16:48 -0400 Subject: [PATCH 07/34] Add tracekv readOperations and getAllKeysUsedInTrace --- baseapp/baseapp.go | 2 +- baseapp/fraudproof.go | 1 - go.mod | 2 ++ go.sum | 2 ++ store/tracekv/store.go | 48 +++++++++++++++++++++++++++++++++++++ store/tracekv/store_test.go | 20 ++++++++++++++++ 6 files changed, 73 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 04c01c61239..67c5fb889f9 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/gogo/protobuf/proto" - smtlib "github.com/lazyledger/smt" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/log" @@ -16,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/store/types" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" diff --git a/baseapp/fraudproof.go b/baseapp/fraudproof.go index ef4e50b18a5..c46f05071b1 100644 --- a/baseapp/fraudproof.go +++ b/baseapp/fraudproof.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/store/mem" "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" - smtlib "github.com/lazyledger/smt" abci "github.com/tendermint/tendermint/abci/types" tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) diff --git a/go.mod b/go.mod index 0f3c2318c88..cd7428313d1 100644 --- a/go.mod +++ b/go.mod @@ -63,6 +63,8 @@ require ( sigs.k8s.io/yaml v1.3.0 ) +require github.com/chrispappas/golang-generics-set v1.0.1 + require ( cloud.google.com/go v0.102.1 // indirect cloud.google.com/go/compute v1.7.0 // indirect diff --git a/go.sum b/go.sum index 1e02b21db30..911c9785940 100644 --- a/go.sum +++ b/go.sum @@ -210,6 +210,8 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chrispappas/golang-generics-set v1.0.1 h1:91l8cInAWTxCPwZ8UNg7qkkPsdFdkYS9hytsd8UJsIU= +github.com/chrispappas/golang-generics-set v1.0.1/go.mod h1:cp8j73+rlDyFF9PrjUkrRvi8L4jSRIsRK6Q1nPPIoqo= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= diff --git a/store/tracekv/store.go b/store/tracekv/store.go index 91f3c657682..210835870d8 100644 --- a/store/tracekv/store.go +++ b/store/tracekv/store.go @@ -1,10 +1,13 @@ package tracekv import ( + "bytes" "encoding/base64" "encoding/json" + "fmt" "io" + "github.com/chrispappas/golang-generics-set/set" "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -17,6 +20,10 @@ const ( iterValueOp operation = "iterValue" ) +var ( + ErrBufferEmpty = fmt.Errorf("provided buffer is empty") +) + type ( // Store implements the KVStore interface with tracing enabled. // Operations are traced on each core KVStore call and written to the @@ -90,6 +97,28 @@ func (tkv *Store) ReverseIterator(start, end []byte) types.Iterator { return tkv.iterator(start, end, false) } +// GetAllKeysUsedInTrace reads through all traced operations and returns +// a set of all the keys inside the trace operations +func (tkv *Store) GetAllKeysUsedInTrace(buf bytes.Buffer) set.Set[string] { + + keys := make(set.Set[string], 0) + for { + traceOp, err := readOperation(&buf) + // Reached end of buffer + if err == ErrBufferEmpty { + return keys + } + if err != nil { + panic(err) + } + key, err := base64.StdEncoding.DecodeString(traceOp.Key) + if err != nil { + panic(errors.Wrap(err, "failed to decode key read from buf")) + } + keys.Add(string(key)) + } +} + // iterator facilitates iteration over a KVStore. It delegates the necessary // calls to it's parent KVStore. func (tkv *Store) iterator(start, end []byte, ascending bool) types.Iterator { @@ -202,3 +231,22 @@ func writeOperation(w io.Writer, op operation, tc types.TraceContext, key, value io.WriteString(w, "\n") } + +// readOperation reads a KVStore operation from the underlying buffer as +// JSON-encoded data where the key/value pair is base64 encoded. +func readOperation(r *bytes.Buffer) (*traceOperation, error) { + raw, err := r.ReadString('\n') + if raw == "" { + return nil, ErrBufferEmpty + } + if err != nil { + return nil, errors.Wrap(err, "failed to read trace operation") + } + traceOp := traceOperation{} + err = json.Unmarshal([]byte(raw), &traceOp) + if err != nil { + return nil, errors.Wrap(err, "failed to deserialize trace operation") + } + + return &traceOp, nil +} diff --git a/store/tracekv/store_test.go b/store/tracekv/store_test.go index 1b81e89bafd..41fe24a6d42 100644 --- a/store/tracekv/store_test.go +++ b/store/tracekv/store_test.go @@ -6,6 +6,7 @@ import ( "io" "testing" + "github.com/chrispappas/golang-generics-set/set" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" @@ -113,6 +114,25 @@ func TestTraceKVStoreSet(t *testing.T) { require.Panics(t, func() { store.Set(nil, []byte("value")) }, "setting a nil key should panic") } +func TestGetAllKeysUsedInTrace(t *testing.T) { + expectedKeys := set.FromSlice([]string{ + string(kvPairs[0].Key), + string(kvPairs[1].Key), + string(kvPairs[2].Key), + }) + + var buf bytes.Buffer + store := newEmptyTraceKVStore(&buf) + buf.Reset() + + for _, kvPair := range kvPairs { + store.Set(kvPair.Key, kvPair.Value) + } + + keys := store.GetAllKeysUsedInTrace(buf) + require.Equal(t, expectedKeys, keys) +} + func TestTraceKVStoreDelete(t *testing.T) { testCases := []struct { key []byte From a841e31265f743d8764421a9d54bcc9bd4ecfb41 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 11:42:36 -0400 Subject: [PATCH 08/34] Cleanup errors --- baseapp/abci.go | 68 ++++++++++++++++++++ baseapp/baseapp.go | 135 ++++++++++++++++++++++++++++------------ baseapp/baseapp_test.go | 16 ++--- 3 files changed, 170 insertions(+), 49 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 0d163113336..16ce567f391 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -143,6 +143,74 @@ func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGet } func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res abci.ResponseGenerateFraudProof) { + // Get an app with tracing with block reverted to previous state + _ = app.cms.(*rootmulti.Store) + + /* + appWithTracing, storeKeyToSubstoreTraceBuf, err := app.enableFraudProofGenerationMode(cms.GetStoreKeys(), app.routerOpts) + if err != nil { + panic(err) + } + + // Run this tracing-enabled app through the set of all nonFradulent and fraudulent state transitions + beginBlockRequest := req.BeginBlockRequest + beginBlockRequest.Header.Height = 1 + isBeginBlockFraudulent := req.DeliverTxRequests == nil + isDeliverTxFraudulent := req.EndBlockRequest == nil + appWithTracing.BeginBlock(beginBlockRequest) + if !isBeginBlockFraudulent { + // BeginBlock is not the fraudulent state transition + appWithTracing.executeNonFraudulentTransactions(req) + + // Remove traces made by all non-fraudulent state transitions + for _, buf := range storeKeyToSubstoreTraceBuf { + buf.Reset() + } + + // Record the trace made by the fraudulent state transitions + if isDeliverTxFraudulent { + // The last DeliverTx is the fraudulent state transition + fraudulentDeliverTx := req.DeliverTxRequests[len(req.DeliverTxRequests)-1] + appWithTracing.DeliverTx(*fraudulentDeliverTx) + } else { + // EndBlock is the fraudulent state transition + appWithTracing.EndBlock(*req.EndBlockRequest) + } + } + + // SubStore trace buffers now record the trace made by the fradulent state transition + + // Get a new app with block reverted to previous state + appFraudGen, _, err := app.enableFraudProofGenerationMode(cms.GetStoreKeys(), app.routerOpts) + if err != nil { + panic(err) + } + + // Fast-forward to right before fradulent state transition occured + appFraudGen.BeginBlock(beginBlockRequest) + if !isBeginBlockFraudulent { + appFraudGen.executeNonFraudulentTransactions(req) + } + + // Export the app's current trace-filtered state into a Fraud Proof and return it + fraudProof, err := appFraudGen.getFraudProof(storeKeyToSubstoreTraceBuf, app.LastBlockHeight()) + if err != nil { + panic(err) + } + + if isBeginBlockFraudulent { + fraudProof.fraudulentBeginBlock = &beginBlockRequest + } else if isDeliverTxFraudulent { + fraudProof.fraudulentDeliverTx = req.DeliverTxRequests[len(req.DeliverTxRequests)-1] + } else { + fraudProof.fraudulentEndBlock = req.EndBlockRequest + } + abciFraudProof := fraudProof.toABCI() + res = abci.ResponseGenerateFraudProof{ + FraudProof: &abciFraudProof, + } + return res + */ return abci.ResponseGenerateFraudProof{} } diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 67c5fb889f9..6196e413a5a 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -1,6 +1,7 @@ package baseapp import ( + "bytes" "fmt" "strings" @@ -840,45 +841,97 @@ func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) { return proto.Marshal(&sdk.TxMsgData{MsgResponses: msgResponses}) } -// set up a new baseapp from given params -func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToSMT map[string]*smtlib.SparseMerkleTree, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { - storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) - storeKeyToSubstoreHash := make(map[string][]byte) - for _, storeKeyName := range storeKeyNames { - storeKey := sdk.NewKVStoreKey(storeKeyName) - storeKeys = append(storeKeys, storeKey) - subStore := storeToLoadFrom[storeKeyName] - it := subStore.Iterator(nil, nil) - substoreSMT := storeKeyToSMT[storeKeyName] - for ; it.Valid(); it.Next() { - key, val := it.Key(), it.Value() - proof, err := substoreSMT.Prove(key) - if err != nil { - return nil, err - } - options = append(options, SetDeepSMTBranchKVPair(storeKey, substoreSMT.Root(), proof, key, val)) - } - storeKeyToSubstoreHash[storeKeyName] = substoreSMT.Root() - } - - options = append(options, SetSubstoresWithRoots(storeKeyToSubstoreHash, storeKeys...)) - - // This initial height is used in `BeginBlock` in `validateHeight` - options = append(options, SetInitialHeight(blockHeight)) - - // make list of options to pass by parsing fraudproof - app := NewBaseApp(appName, logger, db, txDecoder, options...) - // stores are mounted - err := app.Init() - - return app, err -} - -// set up a new baseapp from a fraudproof -func SetupBaseAppFromFraudProof(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, fraudProof FraudProof, options ...func(*BaseApp)) (*BaseApp, error) { - storeKeyToSMT, err := fraudProof.getSubstoreSMTs() +// enableFraudProofGenerationMode rolls back an app's state to a previous +// state and enables tracing for the list of store keys +// It returns the tracing-enabled app along with the trace buffers used +func (app *BaseApp) enableFraudProofGenerationMode(storeKeys []storetypes.StoreKey, routerOpts map[string]func(*BaseApp)) (*BaseApp, map[string]*bytes.Buffer, error) { + cms := app.cms.(*rootmulti.Store) + err := cms.LoadLatestVersion() if err != nil { - return nil, err - } - return SetupBaseAppFromParams(appName, logger, db, txDecoder, fraudProof.getModules(), storeKeyToSMT, fraudProof.blockHeight, fraudProof.extractStore(), options...) -} + return nil, nil, err + } + + return nil, nil, nil + + // // Add options for tracing + // storeTraceBuf := &bytes.Buffer{} + + // storeKeyToSubstoreTraceBuf := make(map[string]*bytes.Buffer) + + // // Initialize params from previousCMS + // storeToLoadFrom := make(map[string]types.KVStore) + // storeKeyToSMT := make(map[string]*smtlib.SparseMerkleTree) + // storeKeyNames := make([]string, 0, len(storeKeys)) + // for _, storeKey := range storeKeys { + // storeKeyName := storeKey.Name() + // storeKeyNames = append(storeKeyNames, storeKeyName) + // storeToLoadFrom[storeKeyName] = previousCMS.GetKVStore(storeKey) + // storeKeyToSMT[storeKeyName] = previousCMS.GetSubstoreSMT(storeKeyName).GetTree() + // storeKeyToSubstoreTraceBuf[storeKeyName] = &bytes.Buffer{} + // } + + // // BaseApp, B1 + // options := []AppOption{ + // SetSubstoreTracer(storeTraceBuf), + // } + + // for _, storeKey := range storeKeys { + // if substoreBuf, exists := storeKeyToSubstoreTraceBuf[storeKey.Name()]; exists { + // options = append(options, SetTracerFor(storeKey.Name(), substoreBuf)) + // } + // if routerOpt, exists := routerOpts[(storeKey.Name())]; exists { + // options = append(options, AppOptionFunc(routerOpt)) + // } + // } + // newApp, err := SetupBaseAppFromParams(app.name+"WithTracing", app.logger, dbm.NewMemDB(), app.txDecoder, storeKeyNames, storeKeyToSMT, app.LastBlockHeight(), storeToLoadFrom, options...) + + // // Need to reset all the buffers to remove anything logged while setting up baseapp + // storeTraceBuf.Reset() + // for _, storeKey := range storeKeys { + // storeKeyToSubstoreTraceBuf[storeKey.Name()].Reset() + // } + // return newApp, storeKeyToSubstoreTraceBuf, err +} + +// // set up a new baseapp from given params +// func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToSMT map[string]*smtlib.SparseMerkleTree, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { +// storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) +// storeKeyToSubstoreHash := make(map[string][]byte) +// for _, storeKeyName := range storeKeyNames { +// storeKey := sdk.NewKVStoreKey(storeKeyName) +// storeKeys = append(storeKeys, storeKey) +// subStore := storeToLoadFrom[storeKeyName] +// it := subStore.Iterator(nil, nil) +// substoreSMT := storeKeyToSMT[storeKeyName] +// for ; it.Valid(); it.Next() { +// key, val := it.Key(), it.Value() +// proof, err := substoreSMT.Prove(key) +// if err != nil { +// return nil, err +// } +// options = append(options, SetDeepSMTBranchKVPair(storeKey, substoreSMT.Root(), proof, key, val)) +// } +// storeKeyToSubstoreHash[storeKeyName] = substoreSMT.Root() +// } + +// options = append(options, SetSubstoresWithRoots(storeKeyToSubstoreHash, storeKeys...)) + +// // This initial height is used in `BeginBlock` in `validateHeight` +// options = append(options, SetInitialHeight(blockHeight)) + +// // make list of options to pass by parsing fraudproof +// app := NewBaseApp(appName, logger, db, txDecoder, options...) +// // stores are mounted +// err := app.Init() + +// return app, err +// } + +// // set up a new baseapp from a fraudproof +// func SetupBaseAppFromFraudProof(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, fraudProof FraudProof, options ...func(*BaseApp)) (*BaseApp, error) { +// storeKeyToSMT, err := fraudProof.getSubstoreSMTs() +// if err != nil { +// return nil, err +// } +// return SetupBaseAppFromParams(appName, logger, db, txDecoder, fraudProof.getModules(), storeKeyToSMT, fraudProof.blockHeight, fraudProof.extractStore(), options...) +// } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 3234ec56109..48f2749312d 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -2311,8 +2311,8 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { 4. Bad block, fraud proof needed, fraud proof works, chain halts (happy case) */ - storeTraceBuf := &bytes.Buffer{} - subStoreTraceBuf := &bytes.Buffer{} + // storeTraceBuf := &bytes.Buffer{} + // subStoreTraceBuf := &bytes.Buffer{} routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { @@ -2391,12 +2391,12 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { // Now we take contents of the fraud proof which was recorded with S2 and try to populate a fresh baseapp B2 with it // B2 <- S2 - codec := codec.NewLegacyAmino() - registerTestCodec(codec) - appB2, err := SetupBaseAppFromFraudProof(t.Name(), defaultLogger(), dbm.NewMemDB(), testTxDecoder(codec), fraudProof, routerOpt) - require.Nil(t, err) - appB2Hash := appB2.cms.(*rootmulti.Store).GetAppHash() - require.Equal(t, appHashB1, appB2Hash) + // codec := codec.NewLegacyAmino() + // registerTestCodec(codec) + // appB2, err := SetupBaseAppFromFraudProof(t.Name(), defaultLogger(), dbm.NewMemDB(), testTxDecoder(codec), fraudProof, routerOpt) + // require.Nil(t, err) + // appB2Hash := appB2.cms.(*rootmulti.Store).GetAppHash() + // require.Equal(t, appHashB1, appB2Hash) // storeHashB2 := appB2.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() // require.Equal(t, storeHashB1, storeHashB2) } From afda438f5793dc8de959d90c1b2d58eb60e11347 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 13:55:11 -0400 Subject: [PATCH 09/34] Add loadLastVersion to IAVL rootmulti store --- store/rootmulti/store.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 2dc559843fc..0f8e3a9838c 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -193,6 +193,18 @@ func (rs *Store) LoadLatestVersion() error { return rs.loadVersion(ver, nil) } +func (rs *Store) LoadLastVersion() error { + + if rs.lastCommitInfo.GetVersion() == 0 { + // This case means that no commit has been made in the store, so + // there is no last version. + return fmt.Errorf("no previous commit found") + } + + lastVersion := rs.lastCommitInfo.GetVersion() + return rs.loadVersion(lastVersion, nil) +} + // LoadVersion implements CommitMultiStore. func (rs *Store) LoadVersion(ver int64) error { return rs.loadVersion(ver, nil) From e9e96ef670fdcce99ac0ae6820a25951b7e8e44d Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 13:55:29 -0400 Subject: [PATCH 10/34] Remove enableFraudProofGenerationMode --- baseapp/baseapp.go | 53 ---------------------------------------------- 1 file changed, 53 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 6196e413a5a..aa033f3d92f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -1,7 +1,6 @@ package baseapp import ( - "bytes" "fmt" "strings" @@ -841,58 +840,6 @@ func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) { return proto.Marshal(&sdk.TxMsgData{MsgResponses: msgResponses}) } -// enableFraudProofGenerationMode rolls back an app's state to a previous -// state and enables tracing for the list of store keys -// It returns the tracing-enabled app along with the trace buffers used -func (app *BaseApp) enableFraudProofGenerationMode(storeKeys []storetypes.StoreKey, routerOpts map[string]func(*BaseApp)) (*BaseApp, map[string]*bytes.Buffer, error) { - cms := app.cms.(*rootmulti.Store) - err := cms.LoadLatestVersion() - if err != nil { - return nil, nil, err - } - - return nil, nil, nil - - // // Add options for tracing - // storeTraceBuf := &bytes.Buffer{} - - // storeKeyToSubstoreTraceBuf := make(map[string]*bytes.Buffer) - - // // Initialize params from previousCMS - // storeToLoadFrom := make(map[string]types.KVStore) - // storeKeyToSMT := make(map[string]*smtlib.SparseMerkleTree) - // storeKeyNames := make([]string, 0, len(storeKeys)) - // for _, storeKey := range storeKeys { - // storeKeyName := storeKey.Name() - // storeKeyNames = append(storeKeyNames, storeKeyName) - // storeToLoadFrom[storeKeyName] = previousCMS.GetKVStore(storeKey) - // storeKeyToSMT[storeKeyName] = previousCMS.GetSubstoreSMT(storeKeyName).GetTree() - // storeKeyToSubstoreTraceBuf[storeKeyName] = &bytes.Buffer{} - // } - - // // BaseApp, B1 - // options := []AppOption{ - // SetSubstoreTracer(storeTraceBuf), - // } - - // for _, storeKey := range storeKeys { - // if substoreBuf, exists := storeKeyToSubstoreTraceBuf[storeKey.Name()]; exists { - // options = append(options, SetTracerFor(storeKey.Name(), substoreBuf)) - // } - // if routerOpt, exists := routerOpts[(storeKey.Name())]; exists { - // options = append(options, AppOptionFunc(routerOpt)) - // } - // } - // newApp, err := SetupBaseAppFromParams(app.name+"WithTracing", app.logger, dbm.NewMemDB(), app.txDecoder, storeKeyNames, storeKeyToSMT, app.LastBlockHeight(), storeToLoadFrom, options...) - - // // Need to reset all the buffers to remove anything logged while setting up baseapp - // storeTraceBuf.Reset() - // for _, storeKey := range storeKeys { - // storeKeyToSubstoreTraceBuf[storeKey.Name()].Reset() - // } - // return newApp, storeKeyToSubstoreTraceBuf, err -} - // // set up a new baseapp from given params // func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToSMT map[string]*smtlib.SparseMerkleTree, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { // storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) From 255115bbd9d87a270afa2ae2ac1680685a76d11c Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 14:35:36 -0400 Subject: [PATCH 11/34] Add substore tracing to rootmulti store --- baseapp/abci.go | 11 +++++------ baseapp/baseapp_test.go | 9 ++++----- baseapp/options.go | 6 ++++++ store/cachemulti/store.go | 6 ++++++ store/rootmulti/store.go | 18 +++++++++++++++++- store/types/store.go | 5 +++++ 6 files changed, 43 insertions(+), 12 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 16ce567f391..781a277864d 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -144,13 +144,12 @@ func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGet func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res abci.ResponseGenerateFraudProof) { // Get an app with tracing with block reverted to previous state - _ = app.cms.(*rootmulti.Store) - + cms := app.cms.(*rootmulti.Store) + err := cms.LoadLastVersion() + if err != nil { + panic(err) + } /* - appWithTracing, storeKeyToSubstoreTraceBuf, err := app.enableFraudProofGenerationMode(cms.GetStoreKeys(), app.routerOpts) - if err != nil { - panic(err) - } // Run this tracing-enabled app through the set of all nonFradulent and fraudulent state transitions beginBlockRequest := req.BeginBlockRequest diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 48f2749312d..61ca7bc1dc6 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -2311,8 +2311,8 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { 4. Bad block, fraud proof needed, fraud proof works, chain halts (happy case) */ - // storeTraceBuf := &bytes.Buffer{} - // subStoreTraceBuf := &bytes.Buffer{} + storeTraceBuf := &bytes.Buffer{} + subStoreTraceBuf := &bytes.Buffer{} routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { @@ -2325,10 +2325,9 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { // BaseApp, B1 appB1 := setupBaseApp(t, routerOpt, - // TODO: Write IAVL equivalent options somehow - // SetSubstoreTracer(storeTraceBuf), - // SetTracerFor(capKey2.Name(), subStoreTraceBuf), ) + appB1.SetCommitMultiStoreTracer(storeTraceBuf) + appB1.SetCommitKVStoreTracer(capKey2.Name(), subStoreTraceBuf) // B1 <- S0 appB1.InitChain(abci.RequestInitChain{}) diff --git a/baseapp/options.go b/baseapp/options.go index ad8b7f9d5e7..6a52e27ae15 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -196,6 +196,12 @@ func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { app.cms.SetTracer(w) } +// SetCommitKVStoreTracer sets the store tracer on the BaseApp's underlying +// tracer for a substore with given skey. +func (app *BaseApp) SetCommitKVStoreTracer(skey string, w io.Writer) { + app.cms.SetTracerFor(skey, w) +} + // SetStoreLoader allows us to customize the rootMultiStore initialization. func (app *BaseApp) SetStoreLoader(loader StoreLoader) { if app.sealed { diff --git a/store/cachemulti/store.go b/store/cachemulti/store.go index deb1d46272d..64b8e7aa60f 100644 --- a/store/cachemulti/store.go +++ b/store/cachemulti/store.go @@ -100,6 +100,12 @@ func (cms Store) SetTracer(w io.Writer) types.MultiStore { return cms } +// SetTracerFor sets the tracer for a particular underlying store in the +// Multistore that will utilize to trace operations. A MultiStore is returned. +func (cms Store) SetTracerFor(_ string, _ io.Writer) types.MultiStore { + return cms +} + // SetTracingContext updates the tracing context for the MultiStore by merging // the given context with the existing context by key. Any existing keys will // be overwritten. It is implied that the caller should update the context when diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 0f8e3a9838c..92a03d03a68 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -365,6 +365,16 @@ func (rs *Store) SetTracer(w io.Writer) types.MultiStore { return rs } +// SetTracerFor sets the tracer for a particular underlying store in the +// Multistore that will utilize to trace operations. A MultiStore is returned. +func (rs *Store) SetTracerFor(skey string, w io.Writer) types.MultiStore { + key := rs.keysByName[skey] + storeParams := rs.storesParams[key] + storeParams.traceWriter = w + rs.storesParams[key] = storeParams + return rs +} + // SetTracingContext updates the tracing context for the MultiStore by merging // the given context with the existing context by key. Any existing keys will // be overwritten. It is implied that the caller should update the context when @@ -551,7 +561,11 @@ func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { store := s.(types.KVStore) if rs.TracingEnabled() { - store = tracekv.NewStore(store, rs.traceWriter, rs.getTracingContext()) + if rs.storesParams[key].traceWriter != nil { + store = tracekv.NewStore(store, rs.storesParams[key].traceWriter, rs.getTracingContext()) + } else { + store = tracekv.NewStore(store, rs.traceWriter, rs.getTracingContext()) + } } if rs.ListeningEnabled(key) { store = listenkv.NewStore(store, key, rs.listeners[key]) @@ -1013,6 +1027,8 @@ type storeParams struct { db dbm.DB typ types.StoreType initialVersion uint64 + + traceWriter io.Writer } func GetLatestVersion(db dbm.DB) int64 { diff --git a/store/types/store.go b/store/types/store.go index e3c0f0822ad..0cf153f4047 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -123,6 +123,11 @@ type MultiStore interface { // returned. SetTracer(w io.Writer) MultiStore + // SetTracerFor sets the tracer for a particular underlying store in the + // Multistore that will utilize to trace operations. The modified MultiStore is + // returned. + SetTracerFor(skey string, w io.Writer) MultiStore + // SetTracingContext sets the tracing context for a MultiStore. It is // implied that the caller should update the context when necessary between // tracing operations. The modified MultiStore is returned. From 3730b3cbfdeb96c92008f816759592c0cae3ee88 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 14:49:58 -0400 Subject: [PATCH 12/34] Add ability to reset trace writer buffers after each commit --- store/rootmulti/store.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 92a03d03a68..eb4d3b4f987 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -1,6 +1,7 @@ package rootmulti import ( + "bytes" "fmt" "io" "math" @@ -375,6 +376,21 @@ func (rs *Store) SetTracerFor(skey string, w io.Writer) types.MultiStore { return rs } +func (rs *Store) resetAllTraceWriters() { + buf, ok := rs.traceWriter.(*bytes.Buffer) + if ok { + buf.Reset() + } + for _, storeParams := range rs.storesParams { + if storeParams.traceWriter != nil { + buf, ok := storeParams.traceWriter.(*bytes.Buffer) + if ok { + buf.Reset() + } + } + } +} + // SetTracingContext updates the tracing context for the MultiStore by merging // the given context with the existing context by key. Any existing keys will // be overwritten. It is implied that the caller should update the context when @@ -466,6 +482,7 @@ func (rs *Store) Commit() types.CommitID { } // reset the removalMap rs.removalMap = make(map[types.StoreKey]bool) + rs.resetAllTraceWriters() if err := rs.handlePruning(version); err != nil { panic(err) From f61fc8d053eb5120e2edd2a21d505a8aa28d7cf0 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 15:06:20 -0400 Subject: [PATCH 13/34] Able to get the trace made by fraudulent state transition now --- baseapp/abci.go | 110 +++++++++++++++++++-------------------- store/rootmulti/store.go | 4 +- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 781a277864d..f251523d708 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -142,74 +142,74 @@ func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGet return res } +func (app *BaseApp) executeNonFraudulentTransactions(req abci.RequestGenerateFraudProof) { + nonFraudulentRequests := req.DeliverTxRequests[:len(req.DeliverTxRequests)-1] + for _, deliverTxRequest := range nonFraudulentRequests { + app.DeliverTx(*deliverTxRequest) + } +} + func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res abci.ResponseGenerateFraudProof) { - // Get an app with tracing with block reverted to previous state + // Revert app to previous state cms := app.cms.(*rootmulti.Store) err := cms.LoadLastVersion() if err != nil { panic(err) } - /* - - // Run this tracing-enabled app through the set of all nonFradulent and fraudulent state transitions - beginBlockRequest := req.BeginBlockRequest - beginBlockRequest.Header.Height = 1 - isBeginBlockFraudulent := req.DeliverTxRequests == nil - isDeliverTxFraudulent := req.EndBlockRequest == nil - appWithTracing.BeginBlock(beginBlockRequest) - if !isBeginBlockFraudulent { - // BeginBlock is not the fraudulent state transition - appWithTracing.executeNonFraudulentTransactions(req) - - // Remove traces made by all non-fraudulent state transitions - for _, buf := range storeKeyToSubstoreTraceBuf { - buf.Reset() - } - // Record the trace made by the fraudulent state transitions - if isDeliverTxFraudulent { - // The last DeliverTx is the fraudulent state transition - fraudulentDeliverTx := req.DeliverTxRequests[len(req.DeliverTxRequests)-1] - appWithTracing.DeliverTx(*fraudulentDeliverTx) - } else { - // EndBlock is the fraudulent state transition - appWithTracing.EndBlock(*req.EndBlockRequest) - } - } + // Run the set of all nonFradulent and fraudulent state transitions + beginBlockRequest := req.BeginBlockRequest + isBeginBlockFraudulent := req.DeliverTxRequests == nil + isDeliverTxFraudulent := req.EndBlockRequest == nil + app.BeginBlock(beginBlockRequest) + if !isBeginBlockFraudulent { + // BeginBlock is not the fraudulent state transition + app.executeNonFraudulentTransactions(req) - // SubStore trace buffers now record the trace made by the fradulent state transition + cms.ResetAllTraceWriters() - // Get a new app with block reverted to previous state - appFraudGen, _, err := app.enableFraudProofGenerationMode(cms.GetStoreKeys(), app.routerOpts) - if err != nil { - panic(err) + // Record the trace made by the fraudulent state transitions + if isDeliverTxFraudulent { + // The last DeliverTx is the fraudulent state transition + fraudulentDeliverTx := req.DeliverTxRequests[len(req.DeliverTxRequests)-1] + app.DeliverTx(*fraudulentDeliverTx) + } else { + // EndBlock is the fraudulent state transition + app.EndBlock(*req.EndBlockRequest) } + } - // Fast-forward to right before fradulent state transition occured - appFraudGen.BeginBlock(beginBlockRequest) - if !isBeginBlockFraudulent { - appFraudGen.executeNonFraudulentTransactions(req) - } + // SubStore trace buffers now record the trace made by the fradulent state transition - // Export the app's current trace-filtered state into a Fraud Proof and return it - fraudProof, err := appFraudGen.getFraudProof(storeKeyToSubstoreTraceBuf, app.LastBlockHeight()) - if err != nil { - panic(err) - } + // Revert app to previous state + err = cms.LoadLastVersion() + if err != nil { + panic(err) + } - if isBeginBlockFraudulent { - fraudProof.fraudulentBeginBlock = &beginBlockRequest - } else if isDeliverTxFraudulent { - fraudProof.fraudulentDeliverTx = req.DeliverTxRequests[len(req.DeliverTxRequests)-1] - } else { - fraudProof.fraudulentEndBlock = req.EndBlockRequest - } - abciFraudProof := fraudProof.toABCI() - res = abci.ResponseGenerateFraudProof{ - FraudProof: &abciFraudProof, - } - return res - */ + // Fast-forward to right before fradulent state transition occured + app.BeginBlock(beginBlockRequest) + if !isBeginBlockFraudulent { + app.executeNonFraudulentTransactions(req) + } + + // // Export the app's current trace-filtered state into a Fraud Proof and return it + // fraudProof, err := appFraudGen.getFraudProof(storeKeyToSubstoreTraceBuf, app.LastBlockHeight()) + // if err != nil { + // panic(err) + // } + + // if isBeginBlockFraudulent { + // fraudProof.fraudulentBeginBlock = &beginBlockRequest + // } else if isDeliverTxFraudulent { + // fraudProof.fraudulentDeliverTx = req.DeliverTxRequests[len(req.DeliverTxRequests)-1] + // } else { + // fraudProof.fraudulentEndBlock = req.EndBlockRequest + // } + // abciFraudProof := fraudProof.toABCI() + // res = abci.ResponseGenerateFraudProof{ + // FraudProof: &abciFraudProof, + // } return abci.ResponseGenerateFraudProof{} } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index eb4d3b4f987..f05f7ae22aa 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -376,7 +376,7 @@ func (rs *Store) SetTracerFor(skey string, w io.Writer) types.MultiStore { return rs } -func (rs *Store) resetAllTraceWriters() { +func (rs *Store) ResetAllTraceWriters() { buf, ok := rs.traceWriter.(*bytes.Buffer) if ok { buf.Reset() @@ -482,7 +482,7 @@ func (rs *Store) Commit() types.CommitID { } // reset the removalMap rs.removalMap = make(map[types.StoreKey]bool) - rs.resetAllTraceWriters() + rs.ResetAllTraceWriters() if err := rs.handlePruning(version); err != nil { panic(err) From f47e13fa84046f433506a0db187b8cec46c31f8b Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 15:17:21 -0400 Subject: [PATCH 14/34] Start writing getFraudProof method in BaseApp --- baseapp/abci.go | 34 ++++++++++++++-------------- baseapp/baseapp.go | 49 +++++++++++++++++++++++++++++++++++++++- store/rootmulti/store.go | 16 ++++++++++++- 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index f251523d708..75118755d08 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -193,23 +193,23 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res app.executeNonFraudulentTransactions(req) } - // // Export the app's current trace-filtered state into a Fraud Proof and return it - // fraudProof, err := appFraudGen.getFraudProof(storeKeyToSubstoreTraceBuf, app.LastBlockHeight()) - // if err != nil { - // panic(err) - // } - - // if isBeginBlockFraudulent { - // fraudProof.fraudulentBeginBlock = &beginBlockRequest - // } else if isDeliverTxFraudulent { - // fraudProof.fraudulentDeliverTx = req.DeliverTxRequests[len(req.DeliverTxRequests)-1] - // } else { - // fraudProof.fraudulentEndBlock = req.EndBlockRequest - // } - // abciFraudProof := fraudProof.toABCI() - // res = abci.ResponseGenerateFraudProof{ - // FraudProof: &abciFraudProof, - // } + // Export the app's current trace-filtered state into a Fraud Proof and return it + fraudProof, err := app.getFraudProof() + if err != nil { + panic(err) + } + + if isBeginBlockFraudulent { + fraudProof.fraudulentBeginBlock = &beginBlockRequest + } else if isDeliverTxFraudulent { + fraudProof.fraudulentDeliverTx = req.DeliverTxRequests[len(req.DeliverTxRequests)-1] + } else { + fraudProof.fraudulentEndBlock = req.EndBlockRequest + } + abciFraudProof := fraudProof.toABCI() + res = abci.ResponseGenerateFraudProof{ + FraudProof: &abciFraudProof, + } return abci.ResponseGenerateFraudProof{} } diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index aa033f3d92f..e87b0c1d2ea 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -15,7 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/rootmulti" - "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/tracekv" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -840,6 +840,53 @@ func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) { return proto.Marshal(&sdk.TxMsgData{MsgResponses: msgResponses}) } +// Generate a fraudproof for an app with the given trace buffers +func (app *BaseApp) getFraudProof() (FraudProof, error) { + fraudProof := FraudProof{} + fraudProof.stateWitness = make(map[string]StateWitness) + fraudProof.blockHeight = app.LastBlockHeight() + cms := app.cms.(*rootmulti.Store) + + appHash := cms.GetAppHash() + fraudProof.appHash = appHash + storeKeys := cms.GetStoreKeys() + for _, storeKey := range storeKeys { + if subStoreTraceBuf := cms.GetTracerBufferFor(storeKey.Name()); subStoreTraceBuf != nil { + keys := cms.GetKVStore(storeKey).(*tracekv.Store).GetAllKeysUsedInTrace(*subStoreTraceBuf) + smt := cms.GetSubstoreSMT(storeKey.Name()) + if smt.Root() == nil { + continue + } + proof, err := cms.GetStoreProof(storeKey.Name()) + if err != nil { + return FraudProof{}, err + } + stateWitness := StateWitness{ + Proof: *proof, + RootHash: smt.Root(), + WitnessData: make([]WitnessData, 0), + } + for key := range keys { + bKey := []byte(key) + has := smt.Has(bKey) + if has { + value := smt.Get(bKey) + proof, err := smt.GetProof(bKey) + if err != nil { + return FraudProof{}, err + } + bVal := []byte(value) + witnessData := WitnessData{bKey, bVal, proof.GetOps()[0]} + stateWitness.WitnessData = append(stateWitness.WitnessData, witnessData) + } + } + fraudProof.stateWitness[storeKey.Name()] = stateWitness + } + } + + return fraudProof, nil +} + // // set up a new baseapp from given params // func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToSMT map[string]*smtlib.SparseMerkleTree, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { // storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index f05f7ae22aa..c93cf7b3880 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -367,7 +367,7 @@ func (rs *Store) SetTracer(w io.Writer) types.MultiStore { } // SetTracerFor sets the tracer for a particular underlying store in the -// Multistore that will utilize to trace operations. A MultiStore is returned. +// Multistore that it will utilize to trace operations. A MultiStore is returned. func (rs *Store) SetTracerFor(skey string, w io.Writer) types.MultiStore { key := rs.keysByName[skey] storeParams := rs.storesParams[key] @@ -376,6 +376,20 @@ func (rs *Store) SetTracerFor(skey string, w io.Writer) types.MultiStore { return rs } +// GetTracerFor gets the tracer for a particular underlying store in the +// Multistore that it will utilize to trace operations. A MultiStore is returned. +func (rs *Store) GetTracerBufferFor(skey string) *bytes.Buffer { + key := rs.keysByName[skey] + storeParams, exists := rs.storesParams[key] + if exists { + buf, ok := storeParams.traceWriter.(*bytes.Buffer) + if ok { + return buf + } + } + return nil +} + func (rs *Store) ResetAllTraceWriters() { buf, ok := rs.traceWriter.(*bytes.Buffer) if ok { From cd8e9cffa0660aefe7e3ed5057dd11c255651423 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 15:32:18 -0400 Subject: [PATCH 15/34] Substitute SMT with IAVL trees inside getFraudProof --- baseapp/baseapp.go | 15 +++++++++------ store/rootmulti/store.go | 8 ++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e87b0c1d2ea..292b45bf8d9 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -853,8 +853,11 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { for _, storeKey := range storeKeys { if subStoreTraceBuf := cms.GetTracerBufferFor(storeKey.Name()); subStoreTraceBuf != nil { keys := cms.GetKVStore(storeKey).(*tracekv.Store).GetAllKeysUsedInTrace(*subStoreTraceBuf) - smt := cms.GetSubstoreSMT(storeKey.Name()) - if smt.Root() == nil { + iavlTree, err := cms.GetIAVLTree(storeKey.Name()) + if err != nil { + return FraudProof{}, err + } + if iavlTree.LastCommitID().Hash == nil { continue } proof, err := cms.GetStoreProof(storeKey.Name()) @@ -863,15 +866,15 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { } stateWitness := StateWitness{ Proof: *proof, - RootHash: smt.Root(), + RootHash: iavlTree.LastCommitID().Hash, WitnessData: make([]WitnessData, 0), } for key := range keys { bKey := []byte(key) - has := smt.Has(bKey) + has := iavlTree.Has(bKey) if has { - value := smt.Get(bKey) - proof, err := smt.GetProof(bKey) + value := iavlTree.Get(bKey) + proof, err := iavlTree.GetProof(bKey) if err != nil { return FraudProof{}, err } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index c93cf7b3880..e4ca8dfc45b 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -1153,3 +1153,11 @@ func flushLatestVersion(batch dbm.Batch, version int64) { batch.Set([]byte(latestVersionKey), bz) } + +func (s *Store) GetIAVLTree(key string) (*iavl.Store, error) { + store := s.GetStoreByName(key) + if store.GetStoreType() != types.StoreTypeIAVL { + return nil, fmt.Errorf("non-IAVL store not supported") + } + return store.(*iavl.Store), nil +} From 3a5ebed0d93c452e65abfd5f8ae562f2dc07ad2a Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 15:44:29 -0400 Subject: [PATCH 16/34] Implement getStoreProof inside rootmulti store --- baseapp/baseapp.go | 7 ++----- store/rootmulti/store.go | 14 ++++++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 292b45bf8d9..710f072a1ea 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -853,17 +853,14 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { for _, storeKey := range storeKeys { if subStoreTraceBuf := cms.GetTracerBufferFor(storeKey.Name()); subStoreTraceBuf != nil { keys := cms.GetKVStore(storeKey).(*tracekv.Store).GetAllKeysUsedInTrace(*subStoreTraceBuf) - iavlTree, err := cms.GetIAVLTree(storeKey.Name()) + iavlTree, err := cms.GetIAVLStore(storeKey.Name()) if err != nil { return FraudProof{}, err } if iavlTree.LastCommitID().Hash == nil { continue } - proof, err := cms.GetStoreProof(storeKey.Name()) - if err != nil { - return FraudProof{}, err - } + proof := cms.GetStoreProof(storeKey.Name()) stateWitness := StateWitness{ Proof: *proof, RootHash: iavlTree.LastCommitID().Hash, diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index e4ca8dfc45b..5b67ac429e1 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -29,6 +29,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/transient" "github.com/cosmos/cosmos-sdk/store/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) const ( @@ -1049,8 +1050,8 @@ func (rs *Store) flushMetadata(db dbm.DB, version int64, cInfo *types.CommitInfo rs.logger.Debug("flushing metadata finished", "height", version) } -func (s *Store) GetAppHash() []byte { - return s.LastCommitID().Hash +func (rs *Store) GetAppHash() []byte { + return rs.LastCommitID().Hash } type storeParams struct { @@ -1154,10 +1155,15 @@ func flushLatestVersion(batch dbm.Batch, version int64) { batch.Set([]byte(latestVersionKey), bz) } -func (s *Store) GetIAVLTree(key string) (*iavl.Store, error) { - store := s.GetStoreByName(key) +func (rs *Store) GetIAVLStore(key string) (*iavl.Store, error) { + store := rs.GetStoreByName(key) if store.GetStoreType() != types.StoreTypeIAVL { return nil, fmt.Errorf("non-IAVL store not supported") } return store.(*iavl.Store), nil } + +func (rs *Store) GetStoreProof(storeKeyName string) *tmcrypto.ProofOp { + proofOp := rs.lastCommitInfo.ProofOp(storeKeyName) + return &proofOp +} From 458750950946b3e1514ea91070e93ea4fe6bba7e Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 15:57:26 -0400 Subject: [PATCH 17/34] Completed generateFraudProof with IAVL store --- baseapp/abci.go | 2 +- baseapp/baseapp.go | 15 ++++++--------- store/iavl/store.go | 6 ++++++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 75118755d08..63566825889 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -210,7 +210,7 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res res = abci.ResponseGenerateFraudProof{ FraudProof: &abciFraudProof, } - return abci.ResponseGenerateFraudProof{} + return res } func (app *BaseApp) generateFraudProofWithRouterOpts(req abci.RequestGenerateFraudProof, routerOpts map[string]func(*BaseApp)) (res abci.ResponseGenerateFraudProof) { diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 710f072a1ea..20d941a480b 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -853,28 +853,25 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { for _, storeKey := range storeKeys { if subStoreTraceBuf := cms.GetTracerBufferFor(storeKey.Name()); subStoreTraceBuf != nil { keys := cms.GetKVStore(storeKey).(*tracekv.Store).GetAllKeysUsedInTrace(*subStoreTraceBuf) - iavlTree, err := cms.GetIAVLStore(storeKey.Name()) + iavlStore, err := cms.GetIAVLStore(storeKey.Name()) if err != nil { return FraudProof{}, err } - if iavlTree.LastCommitID().Hash == nil { + if iavlStore.LastCommitID().Hash == nil { continue } proof := cms.GetStoreProof(storeKey.Name()) stateWitness := StateWitness{ Proof: *proof, - RootHash: iavlTree.LastCommitID().Hash, + RootHash: iavlStore.LastCommitID().Hash, WitnessData: make([]WitnessData, 0), } for key := range keys { bKey := []byte(key) - has := iavlTree.Has(bKey) + has := iavlStore.Has(bKey) if has { - value := iavlTree.Get(bKey) - proof, err := iavlTree.GetProof(bKey) - if err != nil { - return FraudProof{}, err - } + value := iavlStore.Get(bKey) + proof := iavlStore.GetProofFromTree(bKey) bVal := []byte(value) witnessData := WitnessData{bKey, bVal, proof.GetOps()[0]} stateWitness.WitnessData = append(stateWitness.WitnessData, witnessData) diff --git a/store/iavl/store.go b/store/iavl/store.go index 398a66995a5..faac09a7a88 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -286,6 +286,12 @@ func (st *Store) Import(version int64) (*iavl.Importer, error) { return tree.Import(version) } +// Wrapper for getProofFromTree +func (st *Store) GetProofFromTree(key []byte) *tmcrypto.ProofOps { + iavlTree := st.tree.((*iavl.MutableTree)) + return getProofFromTree(iavlTree, key, true) +} + // Handle gatest the latest height, if height is 0 func getHeight(tree Tree, req abci.RequestQuery) int64 { height := req.Height From 3d27e3ebd4e7af458f721fcce7c437182d022f84 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 15:58:53 -0400 Subject: [PATCH 18/34] Remove routerOpts from baseapp test when generating fraud proofs --- baseapp/abci.go | 5 ----- baseapp/baseapp_test.go | 22 +--------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 63566825889..69985c91118 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -213,11 +213,6 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res return res } -func (app *BaseApp) generateFraudProofWithRouterOpts(req abci.RequestGenerateFraudProof, routerOpts map[string]func(*BaseApp)) (res abci.ResponseGenerateFraudProof) { - // app.SetRouterOpts(routerOpts) - return app.GenerateFraudProof(req) -} - func (app *BaseApp) VerifyFraudProof(req abci.RequestVerifyFraudProof) (res abci.ResponseVerifyFraudProof) { return abci.ResponseVerifyFraudProof{} } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 61ca7bc1dc6..b7a6b45abef 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -2354,30 +2354,10 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { //TODO: Write iavl equivalent somehow // storeHashB1 := appB1.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() - routerOpts := make(map[string]func(*BaseApp)) - newRouterOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - kv := msg.(*msgKeyValue) - cms := bapp.cms.(*rootmulti.Store) - sKeys := cms.GetStoreKeys() - largestKey := sKeys[0] - for _, sKey := range sKeys[1:] { - if sKey.Name() > largestKey.Name() { - largestKey = sKey - } - } - bapp.cms.GetKVStore(largestKey).Set(kv.Key, kv.Value) - return &sdk.Result{}, nil - })) - } - routerOpts[capKey2.Name()] = newRouterOpt - - // THINK: No routerOpts in this baseapp version, how do I bypass this requirement? - resp := appB1.generateFraudProofWithRouterOpts( + resp := appB1.GenerateFraudProof( abci.RequestGenerateFraudProof{ BeginBlockRequest: *beginRequest, DeliverTxRequests: append(nonFraudulentDeliverRequests, fraudDeliverRequest), EndBlockRequest: nil, }, - routerOpts, ) // Light Client From afc722984db3a95b6bc45ea59e29a418bd9558a7 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 16:34:26 -0400 Subject: [PATCH 19/34] Switch out proof decoder --- baseapp/fraudproof.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baseapp/fraudproof.go b/baseapp/fraudproof.go index c46f05071b1..61acc4aefca 100644 --- a/baseapp/fraudproof.go +++ b/baseapp/fraudproof.go @@ -128,7 +128,7 @@ func (fraudProof *FraudProof) verifyFraudProof() (bool, error) { if !bytes.Equal(key, proofOp.GetKey()) { return false, fmt.Errorf("got key: %s, expected: %s for storeKey: %s", string(key), string(proof.GetKey()), storeKey) } - proof, err := smt.ProofDecoder(proofOp) + proof, err := types.CommitmentOpDecoder(proofOp) if err != nil { return false, err } From edcefbb961653c84df7511deba256fc3e6bcc961 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 16:34:50 -0400 Subject: [PATCH 20/34] Add Root in IAVL to use working hash instead of lastCommitID hash --- baseapp/baseapp.go | 8 ++++++-- store/iavl/store.go | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 20d941a480b..5e2b03e0f75 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -857,13 +857,17 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { if err != nil { return FraudProof{}, err } - if iavlStore.LastCommitID().Hash == nil { + rootHash, err := iavlStore.Root() + if err != nil { + return FraudProof{}, err + } + if rootHash == nil { continue } proof := cms.GetStoreProof(storeKey.Name()) stateWitness := StateWitness{ Proof: *proof, - RootHash: iavlStore.LastCommitID().Hash, + RootHash: rootHash, WitnessData: make([]WitnessData, 0), } for key := range keys { diff --git a/store/iavl/store.go b/store/iavl/store.go index faac09a7a88..04c778dbd85 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -292,6 +292,12 @@ func (st *Store) GetProofFromTree(key []byte) *tmcrypto.ProofOps { return getProofFromTree(iavlTree, key, true) } +func (st *Store) Root() ([]byte, error) { + iavlTree := st.tree.((*iavl.MutableTree)) + hash, err := iavlTree.WorkingHash() + return hash, err +} + // Handle gatest the latest height, if height is 0 func getHeight(tree Tree, req abci.RequestQuery) int64 { height := req.Height From e0b809fcc1f087490c3349113c9abfa036147351 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 17:12:59 -0400 Subject: [PATCH 21/34] Fix getAppHash to use working hashes of IAVL stores --- baseapp/abci.go | 5 ++++- baseapp/baseapp.go | 10 ++++++++-- baseapp/baseapp_test.go | 3 ++- store/rootmulti/store.go | 39 ++++++++++++++++++++++++++++++++++----- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 69985c91118..d728fd9deab 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -135,7 +135,10 @@ func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGetAppHash) { cms := app.cms.(*rootmulti.Store) - appHash := cms.GetAppHash() + appHash, err := cms.GetAppHash() + if err != nil { + panic(err) + } res = abci.ResponseGetAppHash{ AppHash: appHash, } diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 5e2b03e0f75..331410bc767 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -847,7 +847,10 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { fraudProof.blockHeight = app.LastBlockHeight() cms := app.cms.(*rootmulti.Store) - appHash := cms.GetAppHash() + appHash, err := cms.GetAppHash() + if err != nil { + return FraudProof{}, err + } fraudProof.appHash = appHash storeKeys := cms.GetStoreKeys() for _, storeKey := range storeKeys { @@ -864,7 +867,10 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { if rootHash == nil { continue } - proof := cms.GetStoreProof(storeKey.Name()) + proof, err := cms.GetStoreProof(storeKey.Name()) + if err != nil { + return FraudProof{}, err + } stateWitness := StateWitness{ Proof: *proof, RootHash: rootHash, diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index b7a6b45abef..e1b653f1c56 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -2349,7 +2349,8 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { executeBlockWithRequests(t, appB1, beginRequest, nonFraudulentDeliverRequests, nil, 0) // Save appHash, substoreHash here for comparision later - appHashB1 := appB1.cms.(*rootmulti.Store).GetAppHash() + appHashB1, err := appB1.cms.(*rootmulti.Store).GetAppHash() + require.Nil(t, err) //TODO: Write iavl equivalent somehow // storeHashB1 := appB1.cms.(*multi.Store).GetSubstoreSMT(capKey2.Name()).Root() diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 5b67ac429e1..374541d24d7 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -9,6 +9,7 @@ import ( "strings" "sync" + sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps" iavltree "github.com/cosmos/iavl" protoio "github.com/gogo/protobuf/io" gogotypes "github.com/gogo/protobuf/types" @@ -1050,8 +1051,29 @@ func (rs *Store) flushMetadata(db dbm.DB, version int64, cInfo *types.CommitInfo rs.logger.Debug("flushing metadata finished", "height", version) } -func (rs *Store) GetAppHash() []byte { - return rs.LastCommitID().Hash +func (rs *Store) GetAppHash() ([]byte, error) { + m, err := rs.getWorkingMap() + if err != nil { + return nil, err + } + return sdkmaps.HashFromMap(m), nil +} + +func (rs *Store) getWorkingMap() (map[string][]byte, error) { + stores := rs.stores + m := make(map[string][]byte, len(stores)) + for key := range stores { + name := key.Name() + iavlStore, err := rs.GetIAVLStore(name) + if err != nil { + return nil, err + } + m[name], err = iavlStore.Root() + if err != nil { + return nil, err + } + } + return m, nil } type storeParams struct { @@ -1163,7 +1185,14 @@ func (rs *Store) GetIAVLStore(key string) (*iavl.Store, error) { return store.(*iavl.Store), nil } -func (rs *Store) GetStoreProof(storeKeyName string) *tmcrypto.ProofOp { - proofOp := rs.lastCommitInfo.ProofOp(storeKeyName) - return &proofOp +func (rs *Store) GetStoreProof(storeKeyName string) (*tmcrypto.ProofOp, error) { + m, err := rs.getWorkingMap() + if err != nil { + return nil, err + } + proofOp, err := types.ProofOpFromMap(m, storeKeyName) + if err != nil { + return nil, err + } + return &proofOp, nil } From 7ca28687c652298314cbf6a826aab879c3e27906 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 17:39:06 -0400 Subject: [PATCH 22/34] Implement ABCI method to verify fraud proofs --- baseapp/abci.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index d728fd9deab..d8f789015f8 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -1,6 +1,7 @@ package baseapp import ( + "bytes" "crypto/sha256" "encoding/json" "errors" @@ -14,6 +15,7 @@ import ( "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + db "github.com/tendermint/tm-db" "google.golang.org/grpc/codes" grpcstatus "google.golang.org/grpc/status" @@ -217,7 +219,60 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res } func (app *BaseApp) VerifyFraudProof(req abci.RequestVerifyFraudProof) (res abci.ResponseVerifyFraudProof) { - return abci.ResponseVerifyFraudProof{} + abciFraudProof := req.FraudProof + fraudProof := FraudProof{} + fraudProof.fromABCI(*abciFraudProof) + + // First two levels of verification + success, err := fraudProof.verifyFraudProof() + if err != nil { + panic(err) + } + + if success { + // Third level of verification + + // Setup a new app from fraud proof + appFromFraudProof, err := SetupBaseAppFromFraudProof( + app.Name()+"FromFraudProof", + app.logger, + db.NewMemDB(), + app.txDecoder, + fraudProof, + ) + if err != nil { + panic(err) + } + appFromFraudProof.InitChain(abci.RequestInitChain{}) + appHash := appFromFraudProof.GetAppHash(abci.RequestGetAppHash{}).AppHash + + if !bytes.Equal(fraudProof.appHash, appHash) { + return abci.ResponseVerifyFraudProof{ + Success: false, + } + } + + // Execute fraudulent state transition + if fraudProof.fraudulentBeginBlock != nil { + appFromFraudProof.BeginBlock(*fraudProof.fraudulentBeginBlock) + } else { + // Need to add some dummy begin block here since its a new app + + appFromFraudProof.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: fraudProof.blockHeight}}) + if fraudProof.fraudulentDeliverTx != nil { + appFromFraudProof.DeliverTx(*fraudProof.fraudulentDeliverTx) + } else { + appFromFraudProof.EndBlock(*fraudProof.fraudulentEndBlock) + } + } + + appHash = appFromFraudProof.GetAppHash(abci.RequestGetAppHash{}).AppHash + success = bytes.Equal(appHash, req.ExpectedAppHash) + } + res = abci.ResponseVerifyFraudProof{ + Success: success, + } + return res } // BeginBlock implements the ABCI application interface. From d3279d0e4cb976fcdf32b475773dc53ba2ab9685 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 17:41:35 -0400 Subject: [PATCH 23/34] Start setup BaseApp from IAVL store WIP --- baseapp/baseapp.go | 89 +++++++++++++++++++++++-------------------- baseapp/fraudproof.go | 14 ++++--- 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 331410bc767..3b27a6c5dbc 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -14,6 +14,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/iavl" "github.com/cosmos/cosmos-sdk/store/rootmulti" "github.com/cosmos/cosmos-sdk/store/tracekv" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -894,45 +895,49 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { return fraudProof, nil } -// // set up a new baseapp from given params -// func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToSMT map[string]*smtlib.SparseMerkleTree, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { -// storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) -// storeKeyToSubstoreHash := make(map[string][]byte) -// for _, storeKeyName := range storeKeyNames { -// storeKey := sdk.NewKVStoreKey(storeKeyName) -// storeKeys = append(storeKeys, storeKey) -// subStore := storeToLoadFrom[storeKeyName] -// it := subStore.Iterator(nil, nil) -// substoreSMT := storeKeyToSMT[storeKeyName] -// for ; it.Valid(); it.Next() { -// key, val := it.Key(), it.Value() -// proof, err := substoreSMT.Prove(key) -// if err != nil { -// return nil, err -// } -// options = append(options, SetDeepSMTBranchKVPair(storeKey, substoreSMT.Root(), proof, key, val)) -// } -// storeKeyToSubstoreHash[storeKeyName] = substoreSMT.Root() -// } - -// options = append(options, SetSubstoresWithRoots(storeKeyToSubstoreHash, storeKeys...)) - -// // This initial height is used in `BeginBlock` in `validateHeight` -// options = append(options, SetInitialHeight(blockHeight)) - -// // make list of options to pass by parsing fraudproof -// app := NewBaseApp(appName, logger, db, txDecoder, options...) -// // stores are mounted -// err := app.Init() - -// return app, err -// } - -// // set up a new baseapp from a fraudproof -// func SetupBaseAppFromFraudProof(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, fraudProof FraudProof, options ...func(*BaseApp)) (*BaseApp, error) { -// storeKeyToSMT, err := fraudProof.getSubstoreSMTs() -// if err != nil { -// return nil, err -// } -// return SetupBaseAppFromParams(appName, logger, db, txDecoder, fraudProof.getModules(), storeKeyToSMT, fraudProof.blockHeight, fraudProof.extractStore(), options...) -// } +// set up a new baseapp from given params +func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToIAVLTree map[string]*iavl.Store, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { + storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) + storeKeyToSubstoreHash := make(map[string][]byte) + for _, storeKeyName := range storeKeyNames { + storeKey := sdk.NewKVStoreKey(storeKeyName) + storeKeys = append(storeKeys, storeKey) + subStore := storeToLoadFrom[storeKeyName] + it := subStore.Iterator(nil, nil) + iavlTree := storeKeyToIAVLTree[storeKeyName] + for ; it.Valid(); it.Next() { + key, val := it.Key(), it.Value() + proof, err := iavlTree.Prove(key) + if err != nil { + return nil, err + } + options = append(options, SetDeepSMTBranchKVPair(storeKey, iavlTree.Root(), proof, key, val)) + } + rootHash, err := iavlTree.Root() + if err != nil { + return nil, err + } + storeKeyToSubstoreHash[storeKeyName] = rootHash + } + + options = append(options, SetSubstoresWithRoots(storeKeyToSubstoreHash, storeKeys...)) + + // This initial height is used in `BeginBlock` in `validateHeight` + options = append(options, SetInitialHeight(blockHeight)) + + // make list of options to pass by parsing fraudproof + app := NewBaseApp(appName, logger, db, txDecoder, options...) + // stores are mounted + err := app.Init() + + return app, err +} + +// set up a new baseapp from a fraudproof +func SetupBaseAppFromFraudProof(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, fraudProof FraudProof, options ...func(*BaseApp)) (*BaseApp, error) { + storeKeyToIAVLTree, err := fraudProof.getDeepIAVLTrees() + if err != nil { + return nil, err + } + return SetupBaseAppFromParams(appName, logger, db, txDecoder, fraudProof.getModules(), storeKeyToIAVLTree, fraudProof.blockHeight, fraudProof.extractStore(), options...) +} diff --git a/baseapp/fraudproof.go b/baseapp/fraudproof.go index 61acc4aefca..78128ada7aa 100644 --- a/baseapp/fraudproof.go +++ b/baseapp/fraudproof.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "fmt" + "github.com/cosmos/cosmos-sdk/store/iavl" "github.com/cosmos/cosmos-sdk/store/mem" "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" @@ -56,23 +57,26 @@ func (fraudProof *FraudProof) getModules() []string { return keys } -func (fraudProof *FraudProof) getSubstoreSMTs() (map[string]*smtlib.SparseMerkleTree, error) { - storeKeyToSMT := make(map[string]*smtlib.SparseMerkleTree) +func (fraudProof *FraudProof) getDeepIAVLTrees() (map[string]*iavl.Store, error) { + storeKeyToIAVLTree := make(map[string]*iavl.Store) for storeKey, stateWitness := range fraudProof.stateWitness { rootHash := stateWitness.RootHash + // TODO(manav): Replace with IAVL Deep Subtrees once implementation is done substoreDeepSMT := smtlib.NewDeepSparseMerkleSubTree(smtlib.NewSimpleMap(), smtlib.NewSimpleMap(), sha256.New(), rootHash) for _, witnessData := range stateWitness.WitnessData { proofOp, key, val := witnessData.Proof, witnessData.Key, witnessData.Value - proof, err := smt.ProofDecoder(proofOp) + proof, err := types.CommitmentOpDecoder(proofOp) if err != nil { return nil, err } + // TODO(manav): Replace with an IAVL proof instead of SMT here smtProof := proof.(*smt.ProofOp).GetProof() substoreDeepSMT.AddBranch(smtProof, key, val) } - storeKeyToSMT[storeKey] = substoreDeepSMT.SparseMerkleTree + // TODO(manav): Replace with actual iavl stores + storeKeyToIAVLTree[storeKey] = &iavl.Store{} } - return storeKeyToSMT, nil + return storeKeyToIAVLTree, nil } func (fraudProof *FraudProof) extractStore() map[string]types.KVStore { From 3f51f2111d09e9760b4a242f614d9ef549b1072b Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 17:47:28 -0400 Subject: [PATCH 24/34] Add option to SetInitialHeight --- baseapp/baseapp.go | 4 ++++ baseapp/options.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 3b27a6c5dbc..c3563b01043 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -368,6 +368,10 @@ func (app *BaseApp) setHaltHeight(haltHeight uint64) { app.haltHeight = haltHeight } +func (app *BaseApp) setInitialHeight(initialHeight int64) { + app.initialHeight = initialHeight +} + func (app *BaseApp) setHaltTime(haltTime uint64) { app.haltTime = haltTime } diff --git a/baseapp/options.go b/baseapp/options.go index 6a52e27ae15..fc428b60d90 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -32,6 +32,11 @@ func SetMinGasPrices(gasPricesStr string) func(*BaseApp) { return func(bapp *BaseApp) { bapp.setMinGasPrices(gasPrices) } } +// SetInitialHeight returns a BaseApp option function that sets the initial block height. +func SetInitialHeight(blockHeight int64) func(*BaseApp) { + return func(bapp *BaseApp) { bapp.setInitialHeight(blockHeight) } +} + // SetHaltHeight returns a BaseApp option function that sets the halt block height. func SetHaltHeight(blockHeight uint64) func(*BaseApp) { return func(bapp *BaseApp) { bapp.setHaltHeight(blockHeight) } From 2ee763e53528df6aca82f8e8bff19271cb22813f Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 18:51:36 -0400 Subject: [PATCH 25/34] Initialize baseapp from IAVL trees --- baseapp/baseapp.go | 34 ++++++++-------------------------- baseapp/fraudproof.go | 22 ++++------------------ baseapp/options.go | 11 +++++++++++ store/rootmulti/store.go | 9 +++++++++ 4 files changed, 32 insertions(+), 44 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index c3563b01043..c153c2f2765 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/cosmos/iavl" "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/tmhash" @@ -14,7 +15,6 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" - "github.com/cosmos/cosmos-sdk/store/iavl" "github.com/cosmos/cosmos-sdk/store/rootmulti" "github.com/cosmos/cosmos-sdk/store/tracekv" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -900,40 +900,22 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { } // set up a new baseapp from given params -func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToIAVLTree map[string]*iavl.Store, blockHeight int64, storeToLoadFrom map[string]types.KVStore, options ...AppOption) (*BaseApp, error) { +func SetupBaseAppFromParams(appName string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, storeKeyNames []string, storeKeyToIAVLTree map[string]*iavl.MutableTree, blockHeight int64, options ...func(*BaseApp)) (*BaseApp, error) { storeKeys := make([]storetypes.StoreKey, 0, len(storeKeyNames)) - storeKeyToSubstoreHash := make(map[string][]byte) for _, storeKeyName := range storeKeyNames { - storeKey := sdk.NewKVStoreKey(storeKeyName) - storeKeys = append(storeKeys, storeKey) - subStore := storeToLoadFrom[storeKeyName] - it := subStore.Iterator(nil, nil) + storeKeys = append(storeKeys, sdk.NewKVStoreKey(storeKeyName)) iavlTree := storeKeyToIAVLTree[storeKeyName] - for ; it.Valid(); it.Next() { - key, val := it.Key(), it.Value() - proof, err := iavlTree.Prove(key) - if err != nil { - return nil, err - } - options = append(options, SetDeepSMTBranchKVPair(storeKey, iavlTree.Root(), proof, key, val)) - } - rootHash, err := iavlTree.Root() - if err != nil { - return nil, err - } - storeKeyToSubstoreHash[storeKeyName] = rootHash + options = append(options, SetDeepIAVLTree(storeKeyName, iavlTree)) } - - options = append(options, SetSubstoresWithRoots(storeKeyToSubstoreHash, storeKeys...)) - // This initial height is used in `BeginBlock` in `validateHeight` options = append(options, SetInitialHeight(blockHeight)) - // make list of options to pass by parsing fraudproof app := NewBaseApp(appName, logger, db, txDecoder, options...) + // stores are mounted - err := app.Init() + app.MountStores(storeKeys...) + err := app.LoadLatestVersion() return app, err } @@ -943,5 +925,5 @@ func SetupBaseAppFromFraudProof(appName string, logger log.Logger, db dbm.DB, tx if err != nil { return nil, err } - return SetupBaseAppFromParams(appName, logger, db, txDecoder, fraudProof.getModules(), storeKeyToIAVLTree, fraudProof.blockHeight, fraudProof.extractStore(), options...) + return SetupBaseAppFromParams(appName, logger, db, txDecoder, fraudProof.getModules(), storeKeyToIAVLTree, fraudProof.blockHeight, options...) } diff --git a/baseapp/fraudproof.go b/baseapp/fraudproof.go index 78128ada7aa..aca13dfbef7 100644 --- a/baseapp/fraudproof.go +++ b/baseapp/fraudproof.go @@ -5,10 +5,9 @@ import ( "crypto/sha256" "fmt" - "github.com/cosmos/cosmos-sdk/store/iavl" - "github.com/cosmos/cosmos-sdk/store/mem" "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" + iavltree "github.com/cosmos/iavl" abci "github.com/tendermint/tendermint/abci/types" tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) @@ -57,8 +56,8 @@ func (fraudProof *FraudProof) getModules() []string { return keys } -func (fraudProof *FraudProof) getDeepIAVLTrees() (map[string]*iavl.Store, error) { - storeKeyToIAVLTree := make(map[string]*iavl.Store) +func (fraudProof *FraudProof) getDeepIAVLTrees() (map[string]*iavltree.MutableTree, error) { + storeKeyToIAVLTree := make(map[string]*iavltree.MutableTree) for storeKey, stateWitness := range fraudProof.stateWitness { rootHash := stateWitness.RootHash // TODO(manav): Replace with IAVL Deep Subtrees once implementation is done @@ -74,24 +73,11 @@ func (fraudProof *FraudProof) getDeepIAVLTrees() (map[string]*iavl.Store, error) substoreDeepSMT.AddBranch(smtProof, key, val) } // TODO(manav): Replace with actual iavl stores - storeKeyToIAVLTree[storeKey] = &iavl.Store{} + storeKeyToIAVLTree[storeKey] = &iavltree.MutableTree{} } return storeKeyToIAVLTree, nil } -func (fraudProof *FraudProof) extractStore() map[string]types.KVStore { - store := make(map[string]types.KVStore) - for storeKey, stateWitness := range fraudProof.stateWitness { - subStore := mem.NewStore() - for _, witnessData := range stateWitness.WitnessData { - key, val := witnessData.Key, witnessData.Value - subStore.Set(key, val) - } - store[storeKey] = subStore - } - return store -} - // Returns true only if only one of the three pointers is nil func (fraudProof *FraudProof) checkFraudulentStateTransition() bool { if fraudProof.fraudulentBeginBlock != nil { diff --git a/baseapp/options.go b/baseapp/options.go index fc428b60d90..d9690d52901 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -11,7 +11,9 @@ import ( "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/iavl" ) // File for storing in-package BaseApp optional functions, @@ -37,6 +39,15 @@ func SetInitialHeight(blockHeight int64) func(*BaseApp) { return func(bapp *BaseApp) { bapp.setInitialHeight(blockHeight) } } +// SetDeepIAVLTree sets the storeParam to have the given deep IAVL tree +// for the given skey. +func SetDeepIAVLTree(skey string, iavlTree *iavl.MutableTree) func(*BaseApp) { + return func(bapp *BaseApp) { + cms := bapp.cms.(*rootmulti.Store) + cms.SetDeepIAVLTree(skey, iavlTree) + } +} + // SetHaltHeight returns a BaseApp option function that sets the halt block height. func SetHaltHeight(blockHeight uint64) func(*BaseApp) { return func(bapp *BaseApp) { bapp.setHaltHeight(blockHeight) } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 374541d24d7..0568b4041e0 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -361,6 +361,13 @@ func (rs *Store) SetInterBlockCache(c types.MultiStorePersistentCache) { rs.interBlockCache = c } +func (rs *Store) SetDeepIAVLTree(skey string, iavlTree *iavltree.MutableTree) { + key := rs.keysByName[skey] + storeParams := rs.storesParams[key] + storeParams.deepIAVLTree = iavlTree + rs.storesParams[key] = storeParams +} + // SetTracer sets the tracer for the MultiStore that the underlying // stores will utilize to trace operations. A MultiStore is returned. func (rs *Store) SetTracer(w io.Writer) types.MultiStore { @@ -1082,6 +1089,8 @@ type storeParams struct { typ types.StoreType initialVersion uint64 + deepIAVLTree *iavltree.MutableTree + traceWriter io.Writer } From d3bba6587718c8b341eabb138adc8bed72571a09 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sat, 15 Oct 2022 19:05:50 -0400 Subject: [PATCH 26/34] Use deepIAVLTree in loadVersion --- baseapp/fraudproof.go | 1 + store/iavl/store.go | 7 +++++++ store/rootmulti/store.go | 7 ++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/baseapp/fraudproof.go b/baseapp/fraudproof.go index aca13dfbef7..869f976dee0 100644 --- a/baseapp/fraudproof.go +++ b/baseapp/fraudproof.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "fmt" + smtlib "github.com/celestiaorg/smt" "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" iavltree "github.com/cosmos/iavl" diff --git a/store/iavl/store.go b/store/iavl/store.go index 04c778dbd85..a9cde062b85 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -40,6 +40,13 @@ type Store struct { tree Tree } +// LoadStoreWithDeepIAVLTree returns an IAVL Store as a CommitKVStore with given deep tree. +func LoadStoreWithDeepIAVLTree(tree *iavl.MutableTree) (types.CommitKVStore, error) { + return &Store{ + tree: tree, + }, nil +} + // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the // store's version (id) from the provided DB. An error is returned if the version // fails to load, or if called with a positive version on an empty tree. diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 0568b4041e0..212ae823530 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -956,9 +956,10 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID case types.StoreTypeIAVL: var store types.CommitKVStore var err error - - if params.initialVersion == 0 { - store, err = iavl.LoadStore(db, rs.logger, key, id, rs.lazyLoading, rs.iavlCacheSize, rs.iavlDisableFastNode) + if params.deepIAVLTree != nil { + store, err = iavl.LoadStoreWithDeepIAVLTree(params.deepIAVLTree) + } else if params.initialVersion == 0 { + store, err = iavl.LoadStore(db, rs.logger, key, id, rs.lazyLoading, rs.iavlCacheSize, true) } else { store, err = iavl.LoadStoreWithInitialVersion(db, rs.logger, key, id, rs.lazyLoading, params.initialVersion, rs.iavlCacheSize, rs.iavlDisableFastNode) } From 48b968cd738532a4567b8929244890a54ee47169 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Sun, 16 Oct 2022 10:11:22 -0400 Subject: [PATCH 27/34] Add SetTracerFor to mock store --- server/mock/store.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/mock/store.go b/server/mock/store.go index 2f4d7601001..ca58336866a 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -50,6 +50,10 @@ func (ms multiStore) SetTracer(w io.Writer) sdk.MultiStore { panic("not implemented") } +func (ms multiStore) SetTracerFor(skey string, w io.Writer) sdk.MultiStore { + panic("not implemented") +} + func (ms multiStore) AddListeners(key storetypes.StoreKey, listeners []storetypes.WriteListener) { panic("not implemented") } From f52e1e2271f132ee4dec11e2c09b24dd3acb829e Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 19 Oct 2022 21:20:41 -0400 Subject: [PATCH 28/34] Add gdocs to new ABCI methods --- baseapp/abci.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/baseapp/abci.go b/baseapp/abci.go index d8f789015f8..2d6b7a1ded7 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -134,6 +134,8 @@ func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { } } +// GetAppHash implements the ABCI application interface. It returns an App Hash +// whiich acts as a unique ID of the current state of the BaseApp. func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGetAppHash) { cms := app.cms.(*rootmulti.Store) @@ -154,11 +156,17 @@ func (app *BaseApp) executeNonFraudulentTransactions(req abci.RequestGenerateFra } } +// GenerateFraudProof implements the ABCI application interface. The BaseApp reverts to +// previous state, runs the given fraudulent state transition, and gets the trace representing +// the operations that this state transition makes. It then uses this trace to filter and export +// the pre-fraudulent state transition execution state of the BaseApp and generates a Fraud Proof +// representing it. It returns this generated Fraud Proof. func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res abci.ResponseGenerateFraudProof) { // Revert app to previous state cms := app.cms.(*rootmulti.Store) err := cms.LoadLastVersion() if err != nil { + // Happens when there is no last state to load form panic(err) } @@ -218,6 +226,10 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res return res } +// VerifyFraudProof implements the ABCI application interface. It loads a fresh BaseApp using +// the given Fraud Proof, runs the given fraudulent state transition within the Fraud Proof, +// and gets the app hash representing state of the resulting BaseApp. It returns a boolean +// representing whether this app hash is equivalent to the expected app hash given. func (app *BaseApp) VerifyFraudProof(req abci.RequestVerifyFraudProof) (res abci.ResponseVerifyFraudProof) { abciFraudProof := req.FraudProof fraudProof := FraudProof{} From 536d58bd7edc582a3bc4dcb7d8e345502b4dcbec Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 19 Oct 2022 21:20:57 -0400 Subject: [PATCH 29/34] Refactor from fmt.errorf into errors.New --- baseapp/fraudproof.go | 7 ++++++- store/tracekv/store.go | 5 ++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/baseapp/fraudproof.go b/baseapp/fraudproof.go index 869f976dee0..e33e91e0ee2 100644 --- a/baseapp/fraudproof.go +++ b/baseapp/fraudproof.go @@ -3,6 +3,7 @@ package baseapp import ( "bytes" "crypto/sha256" + "errors" "fmt" smtlib "github.com/celestiaorg/smt" @@ -13,6 +14,10 @@ import ( tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) +var ( + ErrMoreThanOneBlockTypeUsed = errors.New("fraudProof has more than one type of fradulent state transitions marked nil") +) + // Represents a single-round fraudProof type FraudProof struct { // The block height to load state of @@ -92,7 +97,7 @@ func (fraudProof *FraudProof) checkFraudulentStateTransition() bool { func (fraudProof *FraudProof) verifyFraudProof() (bool, error) { if !fraudProof.checkFraudulentStateTransition() { - return false, fmt.Errorf("fraudProof has more than one type of fradulent state transitions marked nil") + return false, ErrMoreThanOneBlockTypeUsed } for storeKey, stateWitness := range fraudProof.stateWitness { proofOp := stateWitness.Proof diff --git a/store/tracekv/store.go b/store/tracekv/store.go index 210835870d8..08cc29fcbe2 100644 --- a/store/tracekv/store.go +++ b/store/tracekv/store.go @@ -4,12 +4,11 @@ import ( "bytes" "encoding/base64" "encoding/json" - "fmt" + "errors" "io" "github.com/chrispappas/golang-generics-set/set" "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/types/errors" ) const ( @@ -21,7 +20,7 @@ const ( ) var ( - ErrBufferEmpty = fmt.Errorf("provided buffer is empty") + ErrBufferEmpty = errors.New("provided buffer is empty") ) type ( From 6a6c8435f1ea69bd75b9761f403bbb5d2ee69c5c Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 19 Oct 2022 21:32:33 -0400 Subject: [PATCH 30/34] Update error packages used --- store/tracekv/store.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/store/tracekv/store.go b/store/tracekv/store.go index 08cc29fcbe2..817dcfb794e 100644 --- a/store/tracekv/store.go +++ b/store/tracekv/store.go @@ -7,6 +7,7 @@ import ( "errors" "io" + sdkerrors "cosmossdk.io/errors" "github.com/chrispappas/golang-generics-set/set" "github.com/cosmos/cosmos-sdk/store/types" ) @@ -112,7 +113,7 @@ func (tkv *Store) GetAllKeysUsedInTrace(buf bytes.Buffer) set.Set[string] { } key, err := base64.StdEncoding.DecodeString(traceOp.Key) if err != nil { - panic(errors.Wrap(err, "failed to decode key read from buf")) + panic(sdkerrors.Wrap(err, "failed to decode key read from buf")) } keys.Add(string(key)) } @@ -221,11 +222,11 @@ func writeOperation(w io.Writer, op operation, tc types.TraceContext, key, value raw, err := json.Marshal(traceOp) if err != nil { - panic(errors.Wrap(err, "failed to serialize trace operation")) + panic(sdkerrors.Wrap(err, "failed to serialize trace operation")) } if _, err := w.Write(raw); err != nil { - panic(errors.Wrap(err, "failed to write trace operation")) + panic(sdkerrors.Wrap(err, "failed to write trace operation")) } io.WriteString(w, "\n") @@ -239,12 +240,12 @@ func readOperation(r *bytes.Buffer) (*traceOperation, error) { return nil, ErrBufferEmpty } if err != nil { - return nil, errors.Wrap(err, "failed to read trace operation") + return nil, sdkerrors.Wrap(err, "failed to read trace operation") } traceOp := traceOperation{} err = json.Unmarshal([]byte(raw), &traceOp) if err != nil { - return nil, errors.Wrap(err, "failed to deserialize trace operation") + return nil, sdkerrors.Wrap(err, "failed to deserialize trace operation") } return &traceOp, nil From d3e0a327458061b0fb9ab4af72b1a6ecd837f39b Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 19 Oct 2022 21:32:50 -0400 Subject: [PATCH 31/34] Update docs to remove SMT references --- baseapp/baseapp_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index e1b653f1c56..8d3eee8ecc1 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -2296,18 +2296,19 @@ func TestGenerateAndLoadFraudProof(t *testing.T) { Tests switch between a baseapp and fraudproof and covers parts of the fraudproof cycle. Steps: 1. Initialize a baseapp, B1, with some state, S0 2. Make some state transition to state S1 by doing some transactions, commit those transactions, and save - resulting SMT root hash + resulting root hash 3. Make another set of state transitions to state S2 but do not commit 4. Generate a fraudproof which should ignore the uncommitted set of transactions, and export S1 into a fraudProof data structure (minimal snapshot) 5. Verify the fraudproof and check verification passes (done in light client) 6. Load a fresh baseapp, B2, with the contents of fraud proof data structure from (4) so it can begin from state S1. - 7. Check if the SMT root hashes of the new app with saved SMT root hash + 7. Check if the root hashes of the new app with root hash Tests to write in future: 1. Block with bad txs: Txs that exceed gas limits, validateBasic fails, unregistered messages (see TestRunInvalidTransaction) 2. Block with invalid appHash at the end - 3. Corrupted Fraud Proof: bad SMT format, insufficient key-value pairs inside SMT needed to verify fraud + 3. Corrupted Fraud Proof: bad underlying store format, insufficient key-value pairs inside the underlying store + needed to verify fraud 4. Bad block, fraud proof needed, fraud proof works, chain halts (happy case) */ From 19edc8e78c70bb474df3dc921b5ed3999b530388 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 19 Oct 2022 21:35:08 -0400 Subject: [PATCH 32/34] Add unit test for checkFraudulentStateTransition --- baseapp/fraudproof_test.go | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 baseapp/fraudproof_test.go diff --git a/baseapp/fraudproof_test.go b/baseapp/fraudproof_test.go new file mode 100644 index 00000000000..d7d74fcbab9 --- /dev/null +++ b/baseapp/fraudproof_test.go @@ -0,0 +1,60 @@ +package baseapp + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/abci/types" +) + +// Tests the checkFraudulentStateTransition for all possible cases. +func TestCheckFraudulentStateTransition(t *testing.T) { + + //Case when only BeginBlock is set + fraudProof := FraudProof{} + fraudProof.fraudulentBeginBlock = &types.RequestBeginBlock{} + result := fraudProof.checkFraudulentStateTransition() + require.True(t, result) + + //Case when only DeliverTx is set + fraudProof = FraudProof{} + fraudProof.fraudulentDeliverTx = &types.RequestDeliverTx{} + result = fraudProof.checkFraudulentStateTransition() + require.True(t, result) + + //Case when only EndBlock is set + fraudProof = FraudProof{} + fraudProof.fraudulentEndBlock = &types.RequestEndBlock{} + result = fraudProof.checkFraudulentStateTransition() + require.True(t, result) + + //Case when both BeginBlock and DeliverTx are set + fraudProof = FraudProof{} + fraudProof.fraudulentBeginBlock = &types.RequestBeginBlock{} + fraudProof.fraudulentDeliverTx = &types.RequestDeliverTx{} + result = fraudProof.checkFraudulentStateTransition() + require.False(t, result) + + //Case when both BeginBlock and EndBlock are set + fraudProof = FraudProof{} + fraudProof.fraudulentBeginBlock = &types.RequestBeginBlock{} + fraudProof.fraudulentEndBlock = &types.RequestEndBlock{} + result = fraudProof.checkFraudulentStateTransition() + require.False(t, result) + + //Case when both DeliverTx and EndBlock are set + fraudProof = FraudProof{} + fraudProof.fraudulentDeliverTx = &types.RequestDeliverTx{} + fraudProof.fraudulentEndBlock = &types.RequestEndBlock{} + result = fraudProof.checkFraudulentStateTransition() + require.False(t, result) + + //Case when both DeliverTx and EndBlock are set + fraudProof = FraudProof{} + fraudProof.fraudulentBeginBlock = &types.RequestBeginBlock{} + fraudProof.fraudulentDeliverTx = &types.RequestDeliverTx{} + fraudProof.fraudulentEndBlock = &types.RequestEndBlock{} + result = fraudProof.checkFraudulentStateTransition() + require.False(t, result) + +} From da7545744758e9aa520e7bbb6cc11cb9837a24ce Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 19 Oct 2022 21:39:46 -0400 Subject: [PATCH 33/34] modify executeNonFraudulentStateTransactions to accomodate case when EndBlock is fraudulent --- baseapp/abci.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index 2d6b7a1ded7..d6c9659eb37 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -149,8 +149,12 @@ func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGet return res } -func (app *BaseApp) executeNonFraudulentTransactions(req abci.RequestGenerateFraudProof) { - nonFraudulentRequests := req.DeliverTxRequests[:len(req.DeliverTxRequests)-1] +func (app *BaseApp) executeNonFraudulentTransactions(req abci.RequestGenerateFraudProof, isDeliverTxFraudulent bool) { + numNonFraudulentRequests := len(req.DeliverTxRequests) + if isDeliverTxFraudulent { + numNonFraudulentRequests = numNonFraudulentRequests - 1 + } + nonFraudulentRequests := req.DeliverTxRequests[:numNonFraudulentRequests] for _, deliverTxRequest := range nonFraudulentRequests { app.DeliverTx(*deliverTxRequest) } @@ -177,7 +181,7 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res app.BeginBlock(beginBlockRequest) if !isBeginBlockFraudulent { // BeginBlock is not the fraudulent state transition - app.executeNonFraudulentTransactions(req) + app.executeNonFraudulentTransactions(req, isDeliverTxFraudulent) cms.ResetAllTraceWriters() @@ -203,7 +207,7 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res // Fast-forward to right before fradulent state transition occured app.BeginBlock(beginBlockRequest) if !isBeginBlockFraudulent { - app.executeNonFraudulentTransactions(req) + app.executeNonFraudulentTransactions(req, isDeliverTxFraudulent) } // Export the app's current trace-filtered state into a Fraud Proof and return it From 7db91316a32cc09b2b472837b634950666621df3 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Tue, 1 Nov 2022 18:06:17 +0100 Subject: [PATCH 34/34] Refactor code --- baseapp/abci.go | 11 ++++++----- baseapp/baseapp.go | 3 +-- store/rootmulti/store.go | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index d6c9659eb37..c13f21dd16e 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -152,7 +152,7 @@ func (app *BaseApp) GetAppHash(req abci.RequestGetAppHash) (res abci.ResponseGet func (app *BaseApp) executeNonFraudulentTransactions(req abci.RequestGenerateFraudProof, isDeliverTxFraudulent bool) { numNonFraudulentRequests := len(req.DeliverTxRequests) if isDeliverTxFraudulent { - numNonFraudulentRequests = numNonFraudulentRequests - 1 + numNonFraudulentRequests-- } nonFraudulentRequests := req.DeliverTxRequests[:numNonFraudulentRequests] for _, deliverTxRequest := range nonFraudulentRequests { @@ -204,7 +204,7 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res panic(err) } - // Fast-forward to right before fradulent state transition occured + // Fast-forward to right before fradulent state transition occurred app.BeginBlock(beginBlockRequest) if !isBeginBlockFraudulent { app.executeNonFraudulentTransactions(req, isDeliverTxFraudulent) @@ -216,11 +216,12 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res panic(err) } - if isBeginBlockFraudulent { + switch { + case isBeginBlockFraudulent: fraudProof.fraudulentBeginBlock = &beginBlockRequest - } else if isDeliverTxFraudulent { + case isDeliverTxFraudulent: fraudProof.fraudulentDeliverTx = req.DeliverTxRequests[len(req.DeliverTxRequests)-1] - } else { + default: fraudProof.fraudulentEndBlock = req.EndBlockRequest } abciFraudProof := fraudProof.toABCI() diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index c153c2f2765..3fd809ac360 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -885,9 +885,8 @@ func (app *BaseApp) getFraudProof() (FraudProof, error) { bKey := []byte(key) has := iavlStore.Has(bKey) if has { - value := iavlStore.Get(bKey) + bVal := iavlStore.Get(bKey) proof := iavlStore.GetProofFromTree(bKey) - bVal := []byte(value) witnessData := WitnessData{bKey, bVal, proof.GetOps()[0]} stateWitness.WitnessData = append(stateWitness.WitnessData, witnessData) } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 212ae823530..19da271a55a 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -956,11 +956,12 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID case types.StoreTypeIAVL: var store types.CommitKVStore var err error - if params.deepIAVLTree != nil { + switch { + case params.deepIAVLTree != nil: store, err = iavl.LoadStoreWithDeepIAVLTree(params.deepIAVLTree) - } else if params.initialVersion == 0 { + case params.initialVersion == 0: store, err = iavl.LoadStore(db, rs.logger, key, id, rs.lazyLoading, rs.iavlCacheSize, true) - } else { + default: store, err = iavl.LoadStoreWithInitialVersion(db, rs.logger, key, id, rs.lazyLoading, params.initialVersion, rs.iavlCacheSize, rs.iavlDisableFastNode) }