Implement portable Arborix container, section directory, nodes section, and Merkle DAG reconstruction utilities in tricu libraries. Add byte/list helper fixes needed for data-first recursion, validate node payloads, duplicate hashes, and closed child references, and expose executable loading from a root hash. Expand binary reader coverage with portable header/section tests, nodes-section parsing, fixture bundle parsing, and execution checks for reconstructed id/not?/map roots. Refresh fixture bundles and remove obsolete fixtures.
655 lines
19 KiB
Plaintext
655 lines
19 KiB
Plaintext
!import "base.tri" !Local
|
|
!import "list.tri" !Local
|
|
!import "bytes.tri" !Local
|
|
!import "binary.tri" !Local
|
|
|
|
arborixMagic = [(65) (82) (66) (79) (82) (73) (88) (0)]
|
|
arborixMajorVersion = [(0) (1)]
|
|
arborixMinorVersion = [(0) (0)]
|
|
arborixManifestSectionId = [(0) (0) (0) (1)]
|
|
arborixNodesSectionId = [(0) (0) (0) (2)]
|
|
|
|
errMissingSection = 4
|
|
errUnsupportedVersion = 5
|
|
errDuplicateSection = 6
|
|
errDuplicateNode = 7
|
|
errInvalidNodePayload = 8
|
|
errMissingNode = 9
|
|
|
|
nodePayloadLeafTag = 0
|
|
nodePayloadStemTag = 1
|
|
nodePayloadForkTag = 2
|
|
|
|
readArborixMagic = (bs : expectBytes arborixMagic bs)
|
|
|
|
readArborixHeader = (bs :
|
|
bindResult (readArborixMagic bs)
|
|
(_ afterMagic :
|
|
bindResult (readBytes 2 afterMagic)
|
|
(majorVersion afterMajor :
|
|
bindResult (readBytes 2 afterMajor)
|
|
(minorVersion afterMinor :
|
|
bindResult (readBytes 4 afterMinor)
|
|
(sectionCount afterSectionCount :
|
|
bindResult (readBytes 8 afterSectionCount)
|
|
(flags afterFlags :
|
|
bindResult (readBytes 8 afterFlags)
|
|
(dirOffset afterDirOffset :
|
|
ok
|
|
(pair majorVersion
|
|
(pair minorVersion
|
|
(pair sectionCount
|
|
(pair flags dirOffset))))
|
|
afterDirOffset)))))))
|
|
|
|
readSectionRecord = (bs :
|
|
bindResult (readBytes 4 bs)
|
|
(sectionId afterSectionId :
|
|
bindResult (readBytes 2 afterSectionId)
|
|
(sectionVersion afterSectionVersion :
|
|
bindResult (readBytes 2 afterSectionVersion)
|
|
(sectionFlags afterSectionFlags :
|
|
bindResult (readBytes 2 afterSectionFlags)
|
|
(compression afterCompression :
|
|
bindResult (readBytes 2 afterCompression)
|
|
(digestAlgorithm afterDigestAlgorithm :
|
|
bindResult (readBytes 8 afterDigestAlgorithm)
|
|
(offset afterOffset :
|
|
bindResult (readBytes 8 afterOffset)
|
|
(length afterLength :
|
|
bindResult (readBytes 32 afterLength)
|
|
(digest afterDigest :
|
|
ok
|
|
(pair sectionId
|
|
(pair sectionVersion
|
|
(pair sectionFlags
|
|
(pair compression
|
|
(pair digestAlgorithm
|
|
(pair offset
|
|
(pair length digest)))))))
|
|
afterDigest)))))))))
|
|
|
|
readSectionDirectory_ = y (self bs sectionCount i acc :
|
|
matchBool
|
|
(ok (reverse acc) bs)
|
|
(bindResult (readSectionRecord bs)
|
|
(sectionRecord afterSectionRecord :
|
|
self afterSectionRecord sectionCount (succ i) (pair sectionRecord acc)))
|
|
(equal? i sectionCount))
|
|
|
|
readSectionDirectory = (sectionCount bs : readSectionDirectory_ bs sectionCount 0 t)
|
|
|
|
sectionRecordId = (sectionRecord :
|
|
matchPair
|
|
(sectionId _ : sectionId)
|
|
sectionRecord)
|
|
|
|
sectionRecordVersion = (sectionRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(sectionVersion _ : sectionVersion)
|
|
payload)
|
|
sectionRecord)
|
|
|
|
sectionRecordFlags = (sectionRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(sectionFlags _ : sectionFlags)
|
|
payload2)
|
|
payload)
|
|
sectionRecord)
|
|
|
|
sectionRecordCompression = (sectionRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(_ payload3 :
|
|
matchPair
|
|
(compression _ : compression)
|
|
payload3)
|
|
payload2)
|
|
payload)
|
|
sectionRecord)
|
|
|
|
sectionRecordDigestAlgorithm = (sectionRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(_ payload3 :
|
|
matchPair
|
|
(_ payload4 :
|
|
matchPair
|
|
(digestAlgorithm _ : digestAlgorithm)
|
|
payload4)
|
|
payload3)
|
|
payload2)
|
|
payload)
|
|
sectionRecord)
|
|
|
|
sectionRecordOffset = (sectionRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(_ payload3 :
|
|
matchPair
|
|
(_ payload4 :
|
|
matchPair
|
|
(_ payload5 :
|
|
matchPair
|
|
(offset _ : offset)
|
|
payload5)
|
|
payload4)
|
|
payload3)
|
|
payload2)
|
|
payload)
|
|
sectionRecord)
|
|
|
|
sectionRecordLength = (sectionRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(_ payload3 :
|
|
matchPair
|
|
(_ payload4 :
|
|
matchPair
|
|
(_ payload5 :
|
|
matchPair
|
|
(_ payload6 :
|
|
matchPair
|
|
(length _ : length)
|
|
payload6)
|
|
payload5)
|
|
payload4)
|
|
payload3)
|
|
payload2)
|
|
payload)
|
|
sectionRecord)
|
|
|
|
sectionRecordDigest = (sectionRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(_ payload3 :
|
|
matchPair
|
|
(_ payload4 :
|
|
matchPair
|
|
(_ payload5 :
|
|
matchPair
|
|
(_ payload6 :
|
|
matchPair
|
|
(_ digest : digest)
|
|
payload6)
|
|
payload5)
|
|
payload4)
|
|
payload3)
|
|
payload2)
|
|
payload)
|
|
sectionRecord)
|
|
|
|
lookupSectionRecord_ = y (self directory sectionId :
|
|
matchList
|
|
nothing
|
|
(sectionRecord rest :
|
|
matchBool
|
|
(just sectionRecord)
|
|
(self rest sectionId)
|
|
(bytesEq? sectionId (sectionRecordId sectionRecord)))
|
|
directory)
|
|
|
|
lookupSectionRecord = (sectionId directory : lookupSectionRecord_ directory sectionId)
|
|
|
|
sectionDirectoryHasId?_ = y (self directory sectionId :
|
|
matchList
|
|
false
|
|
(sectionRecord rest :
|
|
or?
|
|
(bytesEq? sectionId (sectionRecordId sectionRecord))
|
|
(self rest sectionId))
|
|
directory)
|
|
|
|
sectionDirectoryHasId? = (sectionId directory : sectionDirectoryHasId?_ directory sectionId)
|
|
|
|
sectionDirectoryHasDuplicateIds? = y (self directory :
|
|
matchList
|
|
false
|
|
(sectionRecord rest :
|
|
or?
|
|
(sectionDirectoryHasId?_ rest (sectionRecordId sectionRecord))
|
|
(self rest))
|
|
directory)
|
|
|
|
validateSectionDirectory = (directory rest :
|
|
matchBool
|
|
(err errDuplicateSection rest)
|
|
(ok directory rest)
|
|
(sectionDirectoryHasDuplicateIds? directory))
|
|
|
|
byteSlice = (offset length bytes : bytesTake length (bytesDrop offset bytes))
|
|
|
|
natMake = (bit rest :
|
|
matchBool
|
|
0
|
|
(pair bit rest)
|
|
(and? (equal? bit 0) (equal? rest 0)))
|
|
|
|
natAdd = y (self a b :
|
|
triage
|
|
b
|
|
(_ : b)
|
|
(aBit aRest :
|
|
triage
|
|
a
|
|
(_ : a)
|
|
(bBit bRest :
|
|
matchBool
|
|
(natMake 0 (succ (self aRest bRest)))
|
|
(natMake (matchBool (matchBool 0 1 bBit) (matchBool 1 0 bBit) aBit)
|
|
(self aRest bRest))
|
|
(and? (equal? aBit 1) (equal? bBit 1)))
|
|
b)
|
|
a)
|
|
|
|
natDouble = (n : matchBool 0 (pair 0 n) (equal? n 0))
|
|
|
|
natTimes256 = (n :
|
|
natDouble
|
|
(natDouble
|
|
(natDouble
|
|
(natDouble
|
|
(natDouble
|
|
(natDouble
|
|
(natDouble
|
|
(natDouble n))))))))
|
|
|
|
byteNatShiftAppend_ = y (self byte acc i :
|
|
matchBool
|
|
acc
|
|
(triage
|
|
(natMake 0 (self 0 acc (succ i)))
|
|
(_ : acc)
|
|
(bit rest : natMake bit (self rest acc (succ i)))
|
|
byte)
|
|
(equal? i 8))
|
|
|
|
byteNatShiftAppend = (byte acc : byteNatShiftAppend_ byte acc 0)
|
|
|
|
beBytesToNat = (bytes :
|
|
foldl
|
|
(acc byte : byteNatShiftAppend byte acc)
|
|
0
|
|
bytes)
|
|
|
|
u32BEBytesToNat = beBytesToNat
|
|
u64BEBytesToNat = beBytesToNat
|
|
|
|
arborixHeaderMajorVersion = (header :
|
|
matchPair
|
|
(majorVersion _ : majorVersion)
|
|
header)
|
|
|
|
arborixHeaderMinorVersion = (header :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(minorVersion _ : minorVersion)
|
|
payload)
|
|
header)
|
|
|
|
arborixHeaderSectionCount = (header :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(sectionCount _ : sectionCount)
|
|
payload2)
|
|
payload)
|
|
header)
|
|
|
|
arborixHeaderFlags = (header :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(_ payload3 :
|
|
matchPair
|
|
(flags _ : flags)
|
|
payload3)
|
|
payload2)
|
|
payload)
|
|
header)
|
|
|
|
arborixHeaderDirOffset = (header :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ payload2 :
|
|
matchPair
|
|
(_ payload3 :
|
|
matchPair
|
|
(_ dirOffset : dirOffset)
|
|
payload3)
|
|
payload2)
|
|
payload)
|
|
header)
|
|
|
|
validateArborixHeader = (header rest :
|
|
matchBool
|
|
(ok header rest)
|
|
(err errUnsupportedVersion rest)
|
|
(and?
|
|
(bytesEq? arborixMajorVersion (arborixHeaderMajorVersion header))
|
|
(bytesEq? arborixMinorVersion (arborixHeaderMinorVersion header))))
|
|
|
|
readArborixContainer = (bs :
|
|
bindResult (readArborixHeader bs)
|
|
(header afterHeader :
|
|
bindResult (validateArborixHeader header afterHeader)
|
|
(validHeader afterValidHeader :
|
|
bindResult (readSectionDirectory
|
|
(u32BEBytesToNat (arborixHeaderSectionCount validHeader))
|
|
(bytesDrop (u64BEBytesToNat (arborixHeaderDirOffset validHeader)) bs))
|
|
(directory afterDirectory :
|
|
bindResult (validateSectionDirectory directory afterDirectory)
|
|
(validDirectory afterValidDirectory :
|
|
ok (pair validHeader validDirectory) afterValidDirectory)))))
|
|
|
|
sectionRecordOffsetNat = (sectionRecord :
|
|
u64BEBytesToNat (sectionRecordOffset sectionRecord))
|
|
|
|
sectionRecordLengthNat = (sectionRecord :
|
|
u64BEBytesToNat (sectionRecordLength sectionRecord))
|
|
|
|
extractSectionBytes = (sectionRecord containerBytes :
|
|
byteSlice
|
|
(sectionRecordOffsetNat sectionRecord)
|
|
(sectionRecordLengthNat sectionRecord)
|
|
containerBytes)
|
|
|
|
extractSectionBytesResult = (sectionRecord containerBytes rest :
|
|
(sectionBytes :
|
|
matchBool
|
|
(ok sectionBytes rest)
|
|
(err errUnexpectedEof rest)
|
|
(equal? (bytesLength sectionBytes) (sectionRecordLengthNat sectionRecord)))
|
|
(extractSectionBytes sectionRecord containerBytes))
|
|
|
|
lookupSectionBytes = (sectionId directory containerBytes :
|
|
triage
|
|
nothing
|
|
(sectionRecord : just (extractSectionBytes sectionRecord containerBytes))
|
|
(_ _ : nothing)
|
|
(lookupSectionRecord sectionId directory))
|
|
|
|
sectionBytesOrErr = (sectionId directory containerBytes rest :
|
|
triage
|
|
(err errMissingSection rest)
|
|
(sectionRecord : extractSectionBytesResult sectionRecord containerBytes rest)
|
|
(_ _ : err errMissingSection rest)
|
|
(lookupSectionRecord sectionId directory))
|
|
|
|
readArborixSectionBytes = (sectionId bs :
|
|
bindResult (readArborixContainer bs)
|
|
(container afterContainer :
|
|
matchPair
|
|
(_ directory : sectionBytesOrErr sectionId directory bs afterContainer)
|
|
container))
|
|
|
|
readArborixRequiredSections = (bs :
|
|
bindResult (readArborixContainer bs)
|
|
(container afterContainer :
|
|
matchPair
|
|
(_ directory :
|
|
bindResult (sectionBytesOrErr arborixManifestSectionId directory bs afterContainer)
|
|
(manifestBytes _ :
|
|
bindResult (sectionBytesOrErr arborixNodesSectionId directory bs afterContainer)
|
|
(nodesBytes _ :
|
|
ok (pair manifestBytes nodesBytes) afterContainer)))
|
|
container))
|
|
|
|
readNodeRecord = (bs :
|
|
bindResult (readBytes 32 bs)
|
|
(nodeHash afterNodeHash :
|
|
bindResult (readBytes 4 afterNodeHash)
|
|
(payloadLength afterPayloadLength :
|
|
bindResult (readBytes (u32BEBytesToNat payloadLength) afterPayloadLength)
|
|
(payload afterPayload :
|
|
ok
|
|
(pair nodeHash
|
|
(pair payloadLength payload))
|
|
afterPayload))))
|
|
|
|
nodeRecordHash = (nodeRecord :
|
|
matchPair
|
|
(nodeHash _ : nodeHash)
|
|
nodeRecord)
|
|
|
|
nodeRecordPayloadLength = (nodeRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(payloadLength _ : payloadLength)
|
|
payload)
|
|
nodeRecord)
|
|
|
|
nodeRecordPayload = (nodeRecord :
|
|
matchPair
|
|
(_ payload :
|
|
matchPair
|
|
(_ nodePayload : nodePayload)
|
|
payload)
|
|
nodeRecord)
|
|
|
|
nodePayloadKind = (nodePayload : bytesHead nodePayload)
|
|
|
|
nodePayloadHasTag? = (tag nodePayload :
|
|
triage
|
|
false
|
|
(actualTag : byteEq? actualTag tag)
|
|
(_ _ : false)
|
|
(nodePayloadKind nodePayload))
|
|
|
|
nodePayloadLeaf? = (nodePayload : bytesEq? [(0)] nodePayload)
|
|
|
|
nodePayloadStem? = (nodePayload :
|
|
and?
|
|
(nodePayloadHasTag? nodePayloadStemTag nodePayload)
|
|
(equal? (bytesLength nodePayload) 33))
|
|
|
|
nodePayloadFork? = (nodePayload :
|
|
and?
|
|
(nodePayloadHasTag? nodePayloadForkTag nodePayload)
|
|
(equal? (bytesLength nodePayload) 65))
|
|
|
|
nodePayloadValid? = (nodePayload :
|
|
or?
|
|
(nodePayloadLeaf? nodePayload)
|
|
(or?
|
|
(nodePayloadStem? nodePayload)
|
|
(nodePayloadFork? nodePayload)))
|
|
|
|
nodePayloadStemChildHash = (nodePayload : bytesTake 32 (bytesDrop 1 nodePayload))
|
|
nodePayloadForkLeftHash = (nodePayload : bytesTake 32 (bytesDrop 1 nodePayload))
|
|
nodePayloadForkRightHash = (nodePayload : bytesTake 32 (bytesDrop 33 nodePayload))
|
|
|
|
nodeRecordPayloadValid? = (nodeRecord : nodePayloadValid? (nodeRecordPayload nodeRecord))
|
|
|
|
nodeRecordsHaveInvalidPayload? = y (self nodeRecords :
|
|
matchList
|
|
false
|
|
(nodeRecord rest :
|
|
or?
|
|
(not? (nodeRecordPayloadValid? nodeRecord))
|
|
(self rest))
|
|
nodeRecords)
|
|
|
|
nodeRecordsHaveHash? = y (self nodeRecords nodeHash :
|
|
matchList
|
|
false
|
|
(nodeRecord rest :
|
|
or?
|
|
(bytesEq? nodeHash (nodeRecordHash nodeRecord))
|
|
(self rest nodeHash))
|
|
nodeRecords)
|
|
|
|
nodeRecordsHaveDuplicateHashes? = y (self nodeRecords :
|
|
matchList
|
|
false
|
|
(nodeRecord rest :
|
|
or?
|
|
(nodeRecordsHaveHash? rest (nodeRecordHash nodeRecord))
|
|
(self rest))
|
|
nodeRecords)
|
|
|
|
lookupNodeRecord_ = y (self nodeRecords nodeHash :
|
|
matchList
|
|
nothing
|
|
(nodeRecord rest :
|
|
matchBool
|
|
(just nodeRecord)
|
|
(self rest nodeHash)
|
|
(bytesEq? nodeHash (nodeRecordHash nodeRecord)))
|
|
nodeRecords)
|
|
|
|
lookupNodeRecord = (nodeHash nodeRecords : lookupNodeRecord_ nodeRecords nodeHash)
|
|
|
|
nodeRecordChildHashes = (nodeRecord :
|
|
(nodePayload :
|
|
matchBool
|
|
t
|
|
(matchBool
|
|
(pair (nodePayloadStemChildHash nodePayload) t)
|
|
(pair (nodePayloadForkLeftHash nodePayload)
|
|
(pair (nodePayloadForkRightHash nodePayload) t))
|
|
(nodePayloadStem? nodePayload))
|
|
(nodePayloadLeaf? nodePayload))
|
|
(nodeRecordPayload nodeRecord))
|
|
|
|
nodeHashPresent? = (nodeHash nodeRecords : nodeRecordsHaveHash? nodeRecords nodeHash)
|
|
|
|
nodeChildHashesPresent? = y (self childHashes nodeRecords :
|
|
matchList
|
|
true
|
|
(childHash rest :
|
|
and?
|
|
(nodeHashPresent? childHash nodeRecords)
|
|
(self rest nodeRecords))
|
|
childHashes)
|
|
|
|
nodeRecordChildrenPresent? = (nodeRecord nodeRecords :
|
|
nodeChildHashesPresent? (nodeRecordChildHashes nodeRecord) nodeRecords)
|
|
|
|
nodeRecordsClosed? = y (self nodeRecords allNodeRecords :
|
|
matchList
|
|
true
|
|
(nodeRecord rest :
|
|
and?
|
|
(nodeRecordChildrenPresent? nodeRecord allNodeRecords)
|
|
(self rest allNodeRecords))
|
|
nodeRecords)
|
|
|
|
validateNodeRecords = (nodeRecords rest :
|
|
matchBool
|
|
(err errInvalidNodePayload rest)
|
|
(matchBool
|
|
(err errDuplicateNode rest)
|
|
(matchBool
|
|
(ok nodeRecords rest)
|
|
(err errMissingNode rest)
|
|
(nodeRecordsClosed? nodeRecords nodeRecords))
|
|
(nodeRecordsHaveDuplicateHashes? nodeRecords))
|
|
(nodeRecordsHaveInvalidPayload? nodeRecords))
|
|
|
|
readNodeRecords_ = y (self bs nodeCount i acc :
|
|
matchBool
|
|
(ok (reverse acc) bs)
|
|
(bindResult (readNodeRecord bs)
|
|
(nodeRecord afterNodeRecord :
|
|
self afterNodeRecord nodeCount (succ i) (pair nodeRecord acc)))
|
|
(equal? i nodeCount))
|
|
|
|
readNodeRecords = (nodeCount bs : readNodeRecords_ bs nodeCount 0 t)
|
|
|
|
readNodesSection = (bs :
|
|
bindResult (readBytes 8 bs)
|
|
(nodeCount afterNodeCount :
|
|
bindResult (readNodeRecords (u64BEBytesToNat nodeCount) afterNodeCount)
|
|
(nodeRecords afterNodeRecords :
|
|
bindResult (validateNodeRecords nodeRecords afterNodeRecords)
|
|
(validNodeRecords afterValidNodeRecords :
|
|
ok (pair nodeCount validNodeRecords) afterValidNodeRecords))))
|
|
|
|
readNodesSectionComplete = (bs :
|
|
bindResult (readNodesSection bs)
|
|
(nodesSection afterNodesSection :
|
|
matchBool
|
|
(ok nodesSection afterNodesSection)
|
|
(err errUnexpectedBytes afterNodesSection)
|
|
(bytesNil? afterNodesSection)))
|
|
|
|
readArborixNodesSection = (bs :
|
|
bindResult (readArborixContainer bs)
|
|
(container afterContainer :
|
|
matchPair
|
|
(_ directory :
|
|
bindResult (sectionBytesOrErr arborixNodesSectionId directory bs afterContainer)
|
|
(nodesBytes _ :
|
|
bindResult (readNodesSectionComplete nodesBytes)
|
|
(nodesSection _ : ok nodesSection afterContainer)))
|
|
container))
|
|
|
|
nodesSectionCount = (nodesSection :
|
|
matchPair
|
|
(nodeCount _ : nodeCount)
|
|
nodesSection)
|
|
|
|
nodesSectionRecords = (nodesSection :
|
|
matchPair
|
|
(_ nodeRecords : nodeRecords)
|
|
nodesSection)
|
|
|
|
nodeRecordToTreeWith = (self nodeRecords nodeRecord :
|
|
(nodePayload :
|
|
matchBool
|
|
(ok t t)
|
|
(matchBool
|
|
(bindResult (self (nodePayloadStemChildHash nodePayload) nodeRecords)
|
|
(child _ : ok (t child) t))
|
|
(bindResult (self (nodePayloadForkLeftHash nodePayload) nodeRecords)
|
|
(left _ :
|
|
bindResult (self (nodePayloadForkRightHash nodePayload) nodeRecords)
|
|
(right _ : ok (pair left right) t)))
|
|
(nodePayloadStem? nodePayload))
|
|
(nodePayloadLeaf? nodePayload))
|
|
(nodeRecordPayload nodeRecord))
|
|
|
|
nodeHashToTree = y (self nodeHash nodeRecords :
|
|
triage
|
|
(err errMissingNode t)
|
|
(nodeRecord : nodeRecordToTreeWith self nodeRecords nodeRecord)
|
|
(_ _ : err errMissingNode t)
|
|
(lookupNodeRecord nodeHash nodeRecords))
|
|
|
|
readArborixTreeFromHash = (rootHash bs :
|
|
bindResult (readArborixNodesSection bs)
|
|
(nodesSection afterContainer :
|
|
bindResult (nodeHashToTree rootHash (nodesSectionRecords nodesSection))
|
|
(tree _ : ok tree afterContainer)))
|
|
|
|
readArborixExecutableFromHash = readArborixTreeFromHash
|