Skip to content
This repository was archived by the owner on Apr 13, 2022. It is now read-only.

Commit 9594e82

Browse files
author
Nick Waywood
authored
Merge pull request #30 from hyperledger-labs/iterator-methods
Iterator methods
2 parents dfee0f6 + b82ba64 commit 9594e82

File tree

7 files changed

+140
-121
lines changed

7 files changed

+140
-121
lines changed
File renamed without changes.

examples/Marbles.hs renamed to examples/marbles/Marbles.hs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Data.Aeson ( FromJSON
1616
import qualified Data.ByteString as BS
1717
import qualified Data.ByteString.Lazy as LBS
1818
import qualified Data.ByteString.UTF8 as BSU
19+
import Data.Char ( chr )
1920
import Data.Text ( Text, append, pack, unpack )
2021
import qualified Data.Text.Encoding as TSE
2122
import qualified Data.Text.Lazy as TL
@@ -92,7 +93,6 @@ invokeFunc s =
9293
-- queryMarblesWithPagination s parameters
9394
Right (fn, _) -> pure $ errorPayload (pack ("Invoke did not find function: " ++ unpack fn))
9495

95-
-- TODO: implement CreateCompositeKey to index the marble by color
9696
initMarble :: DefaultChaincodeStub -> [Text] -> IO Pb.Response
9797
initMarble s params =
9898
if Prelude.length params == 4
@@ -101,12 +101,19 @@ initMarble s params =
101101
response <- getState s (head params)
102102
-- Check if marble already exists
103103
if BS.length response /= 0
104-
then throwError $ Error $ "This marble already exists: " ++ (unpack $ head params)
105-
else
106-
-- marshal marble to JSON
107-
let marbleJSON = LBS.toStrict $ encode (parseMarble params)
108-
in
109-
putState s (head params) marbleJSON)
104+
then throwError $ Error $ "This marble already exists: " ++ unpack (head params)
105+
else let
106+
-- marshal marble to JSON
107+
marbleJSON = LBS.toStrict $ encode (parseMarble params)
108+
indexName = "color~name"
109+
nullCharByteString = BSU.fromString [ chr 0 ]
110+
in
111+
do
112+
-- we don't care about the response of putState in the success case
113+
_ <- putState s (head params) marbleJSON
114+
colorNameIndexKey
115+
<- ExceptT $ pure $ createCompositeKey s indexName [ params !! 1, params !! 0 ]
116+
putState s colorNameIndexKey nullCharByteString)
110117
else pure $ errorPayload "Incorrect arguments. Need a marble name, color, size and owner"
111118

112119
transferMarble :: DefaultChaincodeStub -> [Text] -> IO Pb.Response
File renamed without changes.

hie.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ cradle:
33
- path: "./src"
44
component: "fabric-chaincode-haskell:lib"
55

6-
- path: "./examples/Fabcar.hs"
6+
- path: "./examples/fabcar/Fabcar.hs"
77
component: "fabric-chaincode-haskell:exe:fabcar-exe"
88

9-
- path: "./examples/Sacc.hs"
9+
- path: "./examples/sacc/Sacc.hs"
1010
component: "fabric-chaincode-haskell:exe:sacc-exe"
1111

12-
- path: "./examples/Marbles.hs"
12+
- path: "./examples/marbles/Marbles.hs"
1313
component: "fabric-chaincode-haskell:exe:marbles-exe"
1414

1515
- path: "./test"

package.yaml

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
name: fabric-chaincode-haskell
2-
version: 0.1.0.0
3-
github: "nwaywood/fabric-chaincode-haskell"
4-
license: Apache-2
5-
author: "Nick Waywood, Allison Irvin"
6-
maintainer: "n.waywood@gmail.com"
7-
copyright: "2020 Nick Waywood"
1+
name: fabric-chaincode-haskell
2+
version: 0.1.0.0
3+
github: "nwaywood/fabric-chaincode-haskell"
4+
license: Apache-2
5+
author: "Nick Waywood, Allison Irvin"
6+
maintainer: "n.waywood@gmail.com"
7+
copyright: "2020 Nick Waywood"
88

99
# Metadata used when publishing your package
1010
# synopsis: Short description of your package
@@ -13,75 +13,75 @@ copyright: "2020 Nick Waywood"
1313
# To avoid duplicated efforts in documentation and dealing with the
1414
# complications of embedding Haddock markup inside cabal files, it is
1515
# common to point users to the README.md file.
16-
description: Please see the README on GitHub at <https://github.com/nwaywood/fabric-chaincode-haskell>
16+
description: Please see the README on GitHub at <https://github.com/nwaywood/fabric-chaincode-haskell>
1717

1818
dependencies:
19-
- base >= 4.7 && < 5
20-
- grpc-haskell-core
21-
- grpc-haskell
22-
- proto3-suite
23-
- proto3-wire
24-
- vector
25-
- bytestring
26-
- text
27-
- deepseq
28-
- containers
29-
- utf8-string
30-
- aeson
31-
- mtl
19+
- base >= 4.7 && < 5
20+
- grpc-haskell-core
21+
- grpc-haskell
22+
- proto3-suite
23+
- proto3-wire
24+
- vector
25+
- bytestring
26+
- text
27+
- deepseq
28+
- containers
29+
- utf8-string
30+
- aeson
31+
- mtl
3232

3333
library:
34-
source-dirs:
35-
- src
36-
- protos-hs
34+
source-dirs:
35+
- src
36+
- protos-hs
3737

3838
executables:
39-
sacc-exe:
40-
main: Sacc.hs
41-
source-dirs: examples
42-
ghc-options:
43-
- -threaded
44-
- -rtsopts
45-
- -with-rtsopts=-N
46-
- -Wall
47-
- -Wincomplete-uni-patterns
48-
- -main-is Sacc
49-
dependencies:
50-
- fabric-chaincode-haskell
51-
marbles-exe:
52-
main: Marbles.hs
53-
source-dirs: examples
54-
ghc-options:
55-
- -threaded
56-
- -rtsopts
57-
- -with-rtsopts=-N
58-
- -Wall
59-
- -Wincomplete-uni-patterns
60-
- -main-is Marbles
61-
dependencies:
62-
- fabric-chaincode-haskell
63-
fabcar-exe:
64-
main: Fabcar.hs
65-
source-dirs: examples
66-
ghc-options:
67-
- -threaded
68-
- -rtsopts
69-
- -with-rtsopts=-N
70-
- -Wall
71-
- -Wincomplete-uni-patterns
72-
- -main-is Fabcar
73-
dependencies:
74-
- fabric-chaincode-haskell
39+
sacc-exe:
40+
main: Sacc.hs
41+
source-dirs: examples/sacc
42+
ghc-options:
43+
- -threaded
44+
- -rtsopts
45+
- -with-rtsopts=-N
46+
- -Wall
47+
- -Wincomplete-uni-patterns
48+
- -main-is Sacc
49+
dependencies:
50+
- fabric-chaincode-haskell
51+
marbles-exe:
52+
main: Marbles.hs
53+
source-dirs: examples/marbles
54+
ghc-options:
55+
- -threaded
56+
- -rtsopts
57+
- -with-rtsopts=-N
58+
- -Wall
59+
- -Wincomplete-uni-patterns
60+
- -main-is Marbles
61+
dependencies:
62+
- fabric-chaincode-haskell
63+
fabcar-exe:
64+
main: Fabcar.hs
65+
source-dirs: examples/fabcar
66+
ghc-options:
67+
- -threaded
68+
- -rtsopts
69+
- -with-rtsopts=-N
70+
- -Wall
71+
- -Wincomplete-uni-patterns
72+
- -main-is Fabcar
73+
dependencies:
74+
- fabric-chaincode-haskell
7575

7676
tests:
77-
fabric-chaincode-haskell-test:
78-
main: Spec.hs
79-
source-dirs: test
80-
ghc-options:
81-
- -threaded
82-
- -rtsopts
83-
- -with-rtsopts=-N
84-
dependencies:
85-
- fabric-chaincode-haskell
86-
- tasty
87-
- tasty-hunit
77+
fabric-chaincode-haskell-test:
78+
main: Spec.hs
79+
source-dirs: test
80+
ghc-options:
81+
- -threaded
82+
- -rtsopts
83+
- -with-rtsopts=-N
84+
dependencies:
85+
- fabric-chaincode-haskell
86+
- tasty
87+
- tasty-hunit

src/Interfaces.hs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ class ChaincodeStubInterface ccs where
124124
-- Call Close() on the returned StateQueryIteratorInterface object when done.
125125
-- The query is re-executed during validation phase to ensure result set
126126
-- has not changed since transaction endorsement (phantom reads detected).
127-
-- getStateByPartialCompositeKey :: ccs -> String -> [String] -> Either Error StateQueryIterator
127+
getStateByPartialCompositeKey :: ccs -> Text -> [Text] -> ExceptT Error IO StateQueryIterator
128+
128129
-- GetStateByPartialCompositeKeyWithPagination queries the state in the ledger based on
129130
-- a given partial composite key. This function returns an iterator
130131
-- which can be used to iterate over the composite keys whose
@@ -143,18 +144,22 @@ class ChaincodeStubInterface ccs where
143144
-- code point). See related functions SplitCompositeKey and CreateCompositeKey.
144145
-- Call Close() on the returned StateQueryIteratorInterface object when done.
145146
-- This call is only supported in a read only transaction.
146-
-- getStateByPartialCompositeKeyWithPagination :: ccs -> String -> [String] -> Int32 -> String -> Either Error (StateQueryIterator, Pb.QueryResponseMetadata)
147+
getStateByPartialCompositeKeyWithPagination
148+
:: ccs -> Text -> [Text] -> Int -> Text -> ExceptT Error IO (StateQueryIterator, Pb.QueryResponseMetadata)
149+
147150
-- CreateCompositeKey combines the given `attributes` to form a composite
148151
-- key. The objectType and attributes are expected to have only valid utf8
149152
-- strings and should not contain U+0000 (nil byte) and U+10FFFF
150153
-- (biggest and unallocated code point).
151154
-- The resulting composite key can be used as the key in PutState().
152-
-- createCompositeKey :: ccs -> String -> [String] -> Either Error String
155+
createCompositeKey :: ccs -> Text -> [Text] -> Either Error Text
156+
153157
-- SplitCompositeKey splits the specified key into attributes on which the
154158
-- composite key was formed. Composite keys found during range queries
155159
-- or partial composite key queries can therefore be split into their
156160
-- composite parts.
157-
-- splitCompositeKey :: ccs -> String -> Either Error (String, [String])
161+
splitCompositeKey :: ccs -> Text -> Either Error (Text, [Text])
162+
158163
-- GetQueryResult performs a "rich" query against a state database. It is
159164
-- only supported for state databases that support rich query,
160165
-- e.g.CouchDB. The query string is in the native syntax
@@ -170,7 +175,8 @@ class ChaincodeStubInterface ccs where
170175
-- be detected at validation/commit time. Applications susceptible to this
171176
-- should therefore not use GetQueryResult as part of transactions that update
172177
-- ledger, and should limit use to read-only chaincode operations.
173-
-- getQueryResult :: ccs -> String -> Either Error StateQueryIterator
178+
getQueryResult :: ccs -> Text -> ExceptT Error IO StateQueryIterator
179+
174180
-- GetQueryResultWithPagination performs a "rich" query against a state database.
175181
-- It is only supported for state databases that support rich query,
176182
-- e.g., CouchDB. The query string is in the native syntax
@@ -184,7 +190,9 @@ class ChaincodeStubInterface ccs where
184190
-- can be used as a value to the bookmark argument. Otherwise, an empty string
185191
-- must be passed as bookmark.
186192
-- This call is only supported in a read only transaction.
187-
-- getQueryResultWithPagination :: ccs -> String -> Int32 -> String -> Either Error (StateQueryIterator, Pb.QueryResponseMetadata)
193+
getQueryResultWithPagination
194+
:: ccs -> Text -> Int -> Text -> ExceptT Error IO (StateQueryIterator, Pb.QueryResponseMetadata)
195+
188196
-- GetHistoryForKey returns a history of key values across time.
189197
-- For each historic key update, the historic value and associated
190198
-- transaction id and timestamp are returned. The timestamp is the
@@ -197,7 +205,9 @@ class ChaincodeStubInterface ccs where
197205
-- detected at validation/commit time. Applications susceptible to this
198206
-- should therefore not use GetHistoryForKey as part of transactions that
199207
-- update ledger, and should limit use to read-only chaincode operations.
200-
-- getHistoryForKey :: ccs -> Either Error HistoryQueryIterator
208+
-- TODO: value should be HistoryQueryIterator
209+
getHistoryForKey :: ccs -> Text -> ExceptT Error IO StateQueryIterator
210+
201211
-- GetPrivateData returns the value of the specified `key` from the specified
202212
-- `collection`. Note that GetPrivateData doesn't read data from the
203213
-- private writeset, which has not been committed to the `collection`. In

src/Stub.hs

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ module Stub where
66

77
import qualified Common.Common as Pb
88

9-
import Control.Monad.Except ( ExceptT(..), runExceptT )
9+
import Control.Monad.Except ( ExceptT(..), runExceptT, throwError )
1010

11-
-- import Data.Int (fromIntegral)
1211
import Data.Bifunctor
1312
import Data.ByteString as BS
1413
import qualified Data.ByteString.Lazy as LBS
14+
import Data.Char ( chr )
1515
import Data.IORef ( modifyIORef, newIORef, readIORef, writeIORef )
16-
import Data.Text
16+
import Data.Text as TS
1717
import Data.Text.Encoding
1818
import Data.Text.Lazy as TL
1919
import Data.Vector as Vector ( (!), Vector, empty, foldr, length, toList )
@@ -137,13 +137,6 @@ instance ChaincodeStubInterface DefaultChaincodeStub where
137137
Right _ -> pure ()
138138
listenForResponse (recvStream ccs)
139139

140-
--
141-
-- -- setStateValidationParameter :: ccs -> String -> [ByteString] -> Maybe Error
142-
-- setStateValidationParameter ccs key parameters = Right notImplemented
143-
--
144-
-- -- getStateValiationParameter :: ccs -> String -> Either Error [ByteString]
145-
-- getStateValiationParameter ccs key = Left notImplemented
146-
--
147140
-- TODO: Implement better error handling/checks etc
148141
-- getStateByRange :: ccs -> Text -> Text -> IO (Either Error StateQueryIterator)
149142
getStateByRange ccs startKey endKey =
@@ -173,7 +166,37 @@ instance ChaincodeStubInterface DefaultChaincodeStub where
173166
Right _ -> pure ()
174167
runExceptT $ ExceptT (listenForResponse (recvStream ccs)) >>= (bsToSqiAndMeta ccs)
175168

176-
-- TODO : implement all these interface functions
169+
-- TODO: This is the next TODO! Implement these 7 function because they are needed in marbles.hs
170+
-- getStateByPartialCompositeKey :: ccs -> Text -> [Text] -> Either Error StateQueryIterator
171+
getStateByPartialCompositeKey ccs objectType keys = throwError $ Error "not implemented"
172+
173+
--getStateByPartialCompositeKeyWithPagination :: ccs -> Text -> [Text] -> Int32 -> Text -> Either Error (StateQueryIterator, Pb.QueryResponseMetadata)
174+
getStateByPartialCompositeKeyWithPagination ccs objectType keys pageSize bookmark =
175+
throwError $ Error "not implemented"
176+
177+
--createCompositeKey :: ccs -> Text -> [Text] -> Either Error Text
178+
createCompositeKey ccs objectType keys =
179+
let keysString = Prelude.foldr (\key acc -> acc ++ TS.unpack key ++ nullCodepoint) "" keys
180+
nullCodepoint = [ chr 0 ]
181+
in
182+
-- TODO: Check that objectTypes and keys are all valid utf8 strings
183+
Right $ TS.pack $ "\x00" ++ TS.unpack objectType ++ nullCodepoint ++ keysString
184+
185+
--splitCompositeKey :: ccs -> Text -> Either Error (Text, [Text])
186+
splitCompositeKey ccs key =
187+
-- key has the form \x00objectTypeU+0000keyU+0000key etc so we use `tail key` to ignore the \x00 char
188+
-- and then split on the unicode codepoint U+0000 to extract the objectType and keys
189+
let keys = TS.splitOn (TS.singleton $ chr 0) (TS.tail key) in Right (Prelude.head keys, Prelude.tail keys)
190+
191+
--getQueryResult :: ccs -> Text -> Either Error StateQueryIterator
192+
getQueryResult ccs query = throwError $ Error "not implemented"
193+
194+
--getQueryResultWithPagination :: ccs -> Text -> Int32 -> Text -> Either Error (StateQueryIterator, Pb.QueryResponseMetadata)
195+
getQueryResultWithPagination ccs key pageSize bookmark = throwError $ Error "not implemented"
196+
197+
--getHistoryForKey :: ccs -> Text -> Either Error HistoryQueryIterator
198+
getHistoryForKey ccs key = throwError $ Error "not implemented"
199+
177200
instance StateQueryIteratorInterface StateQueryIterator where
178201
-- TODO: remove the IO from this function (possibly with the State monad)
179202
-- hasNext :: sqi -> IO Bool
@@ -184,6 +207,7 @@ instance StateQueryIteratorInterface StateQueryIterator where
184207
pure $ (currentLoc < Prelude.length (Pb.queryResponseResults queryResponse))
185208
|| (Pb.queryResponseHasMore queryResponse)
186209

210+
-- TODO : implement close function (need to do anything here in haskell?)
187211
-- close :: sqi -> IO (Maybe Error)
188212
close _ = pure Nothing
189213

@@ -296,28 +320,6 @@ fetchNextQueryResult sqi = do
296320
Left err -> error ("Error while streaming: " ++ show err)
297321
Right _ -> pure ()
298322
runExceptT $ ExceptT (listenForResponse (recvStream $ sqiChaincodeStub sqi)) >>= bsToQueryResponse
299-
--
300-
-- -- getStateByPartialCompositeKey :: ccs -> String -> [String] -> Either Error StateQueryIterator
301-
-- getStateByPartialCompositeKey ccs objectType keys = Left notImplemented
302-
--
303-
-- --getStateByPartialCompositeKeyWithPagination :: ccs -> String -> [String] -> Int32 -> String -> Either Error (StateQueryIterator, Pb.QueryResponseMetadata)
304-
-- getStateByPartialCompositeKeyWithPagination ccs objectType keys pageSize bookmark = Left notImplemented
305-
--
306-
-- --createCompositeKey :: ccs -> String -> [String] -> Either Error String
307-
-- createCompositeKey ccs objectType keys = Left notImplemented
308-
--
309-
-- --splitCompositeKey :: ccs -> String -> Either Error (String, [String])
310-
-- splitCompositeKey ccs key = Left notImplemented
311-
--
312-
-- --getQueryResult :: ccs -> String -> Either Error StateQueryIterator
313-
-- getQueryResult ccs query = Left notImplemented
314-
--
315-
-- --getQueryResultWithPagination :: ccs -> String -> Int32 -> String -> Either Error (StateQueryIterator, Pb.QueryResponseMetadata)
316-
-- getQueryResultWithPagination ccs key pageSize bookmark = Left notImplemented
317-
--
318-
-- --getHistoryForKey :: ccs -> String -> Either Error HistoryQueryIterator
319-
-- getHistoryForKey ccs key = Left notImplemented
320-
--
321323
-- --getPrivateData :: ccs -> String -> String -> Either Error ByteString
322324
-- getPrivateData ccs collection key = Left notImplemented
323325
--

0 commit comments

Comments
 (0)