diff --git a/CHANGELOG.md b/CHANGELOG.md index b232f26..f233d1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- changed: Upgrade sdks to v2.5.2 +- changed: Use floats for scan progress +- changed: Updated checkpoints + ## 0.10.7 (2026-04-24) - changed: Updated checkpoints diff --git a/android/build.gradle b/android/build.gradle index 76017d5..8b1875b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -49,8 +49,8 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.paging:paging-runtime-ktx:2.1.2' - implementation 'cash.z.ecc.android:zcash-android-sdk:2.4.0' - implementation 'cash.z.ecc.android:zcash-android-sdk-incubator:2.4.0' + implementation 'cash.z.ecc.android:zcash-android-sdk:2.5.2' + implementation 'cash.z.ecc.android:zcash-android-sdk-incubator:2.5.2' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" } diff --git a/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3320000.json b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3320000.json new file mode 100644 index 0000000..6e446f6 --- /dev/null +++ b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3320000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "3320000", + "hash": "00000000004fb9b204f85a15d34b0132ccf63db73275017358ffc8bc2268ad7b", + "time": 1777097332, + "saplingTree": "014961b911db6ca9648a19f611b92885e92b0c1370e39c67c9f93bc12efb51b11c01dfb252e9aebbadadec79815def64e191ba9398aa2f136fb9d20b61b4dc604d221f01ad9ff18a8ca63454ddfe55740d68ee9fe10063a74fe5daa1dff535c1f1f186140156b2c8d475ebfd67192a15994bd0ad8c1c6115714e7f4e408f5b0fb07c334069000001b9f0c44da08836c1b840aa10cbf8948c9642a007fe4db280abbbdf564a17f807013a864b735d74ed5a26c7ca7ec2851707e9ed67efb8b60dc2e1c27d334ce9ac44000000017249031a7e8a63373c6f2cca8cfff7c0b165d571a0cc2aa2462ec296053ad50701c7d6949773a176f8662f15980d4aa03c2fe37159b8202ccdbbe7a45b2a97730a013c11c5c16d930a9131cb982f34292600bcb3ad7550b2b7433d2de60f4af1c43a0000011d85b015e936981d3a869c16ed2e03aeeb2444206933acc3f7b384aeb963256601929f0d44045c58428bd8daca3e2c9ca5a0361b5573a4158251dbe9f63a84e94901931c48ea50688eae5e54c24d0df7a6b00ce498f92eb5e1cfd6a2d87d8ebd5364017d1ce2f0839bdbf1bad7ae37f845e7fe2116e0c1197536bfbad549f3876c3c590000013e2598f743726006b8de42476ed56a55a75629a7b82e430c4e7c101a69e9b02a011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e0000000000", + "orchardTree": "01020903639f27b5a404672d457bc910e84ee3a8fb30b06c63d59ae75ffe9f922e01859514ae0fa64c0296548c4b94886803715db4f7428a4029a515334cd659c91d1f000000000000015b02346fe82e10016bf619ed9b58d3901ac07ae19d1b32f6f386876a78e1712c0000017d0b4292bd823ef44a01c5fc7282c098f515c2bd712e7fef9075c60561ae460e00000131e80d81a95e579a9fd47076c28a306adf4a57d220932ab60d709e0d1197353a01e3bfb7711570dbd7c53d2f2282fdef803df6405499fa31b552d26d5e966d1a2501e138d4d2240b194bcee2e11a80b088e02020aceae603de2d45bfc012aac5e4260188443948aa43362e496ad2f4f3b16357f9d8c9e5ffc00e64b55c76fbea01d9380000012829e8aacdf1501baaeb5cb6e189d4e7182228e3d4b9acf54713595241e97f21017c8ece2b2ab2355d809b58809b21c7a5e95cfc693cd689387f7533ec8749261e01cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3330000.json b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3330000.json new file mode 100644 index 0000000..cb9992e --- /dev/null +++ b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3330000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "3330000", + "hash": "000000000023ab2f79bd547abbb231fd0dfb325c01e8384bf4f4378bd226cada", + "time": 1777851068, + "saplingTree": "0194213f428c8e759afdee8eb284a3e03129be7c8d7d538e6db313553ff80f3f1201d69d0e36ff0e355f995a48ec67f502b51cefb57397d6001a3a1784f99f6bd26a1f019fa63f0c50ad63a2cfcbb8708a3cc999233cde27612a2a9a6ecf617c84747351000000011c762f3514aaae599e05e5d91de67851cd35d6dfc3c3bc77f9cf47ed4a77da51000196e56ef5d4601f35444403a09f2e061fe29556aae65891fa1717cfa5b3b20c2b00017afd972f0aff5f609b77288625aa116cb4245340a053582fcb902e9c92ce44080001d0d1c34e478a0ec4a9246fb92aa17a358fd2110c31f65443ceaccc5711197f670001e1ed2cd5a21ca655ae19e2f2c367d0039f37ee35ba856791768e01e8b5012d5b00011d85b015e936981d3a869c16ed2e03aeeb2444206933acc3f7b384aeb963256601929f0d44045c58428bd8daca3e2c9ca5a0361b5573a4158251dbe9f63a84e94901931c48ea50688eae5e54c24d0df7a6b00ce498f92eb5e1cfd6a2d87d8ebd5364017d1ce2f0839bdbf1bad7ae37f845e7fe2116e0c1197536bfbad549f3876c3c590000013e2598f743726006b8de42476ed56a55a75629a7b82e430c4e7c101a69e9b02a011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e0000000000", + "orchardTree": "015ece656504bb32abdd165209fe552335e20656c1e121cbd24f505089bf13610701096f6aca1989167325c41f552965f8f52c59b3ae77973424edbc30ff65208b2a1f015a09701752283479ac2e948d7a1e912526163a895b876dac25c6435c2127eb2d000148fd1916193e59e82cac753a27cbc893f0697cd59c0d300be733f6eeeb432b2d00011f7890fded325ffb243f7c138be018e3fea2ef1d146d5dba47087cb98e3a1918014fbf25637a696d31d7b80b9fc4a8ef4d6211f9159cd931cf71535fa849bedf300001b8750f1a6d3ce7554eecac1a677918c25fdd1d52c0ec38288b77657f7b9edf2501fb87516e6d86726d35ce2277be63f70ab64687c94f01dbdad60deab1ed704d1e01fb690093c87d3bedbcd98d8c2897ae0ea318b1afafe9402c543efc7c68298d330136f488f7280bc738173c50f7096f284dea52a74be925a3010a6f0219928b1f0b0000011b965e10b3835311197d9b8b1f0219607c5adb7b4dfb80878c95e5351ef3b118000001e4bb5b2b7df9f4c3f508da258d91255399df66e84af8795377b7696bb87df42300012829e8aacdf1501baaeb5cb6e189d4e7182228e3d4b9acf54713595241e97f21017c8ece2b2ab2355d809b58809b21c7a5e95cfc693cd689387f7533ec8749261e01cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3340000.json b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3340000.json new file mode 100644 index 0000000..8a794a8 --- /dev/null +++ b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3340000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "3340000", + "hash": "0000000000176c646f484fd89f0849d5d12b2e72fc3b6bb19f28513c131542aa", + "time": 1778604451, + "saplingTree": "017d5d63df6a14192d72dc5e6e2a6a71fe973ca6fcf9a5857593a3fea29f7b9b24001f000001ea8124cfa09f07e8fb7fc4cb0a3b057b4b4632d76e5b5a2d6a036019f290fa5701f9c1a177d696088c1398689bc6f444d8613325e0118158c83c71ac3e1832376101381defcce60fe6d2eda20d3de8c1ccf5b3e1ae4dab9c0a6782faa15fc056960c0164a0c7286c5a9196246d0a0db160c876377cea24154133bcda23acc06ca1fa32015ed6a4099c41ded03f5dbec882a691849d4b62e0c2dff282c7a0250cefbb024c01319578858ea9e3381642e979dd307c59c2aa360392ec75e5a88c23143457f76c01b6dd3f9fe1163acb4750b9dedde2b9be4741b1ebe94cc3a676dac57ec230e461000120cc0c0ff8e16bd54c723a4340007c266b33f0a99ef18aa0a4a2cf3889f0375b01b79e8339dd14c75d334b7d22837c83b54af56dc8ffc00c1d9e000d73218c1f4501e1ed2cd5a21ca655ae19e2f2c367d0039f37ee35ba856791768e01e8b5012d5b00011d85b015e936981d3a869c16ed2e03aeeb2444206933acc3f7b384aeb963256601929f0d44045c58428bd8daca3e2c9ca5a0361b5573a4158251dbe9f63a84e94901931c48ea50688eae5e54c24d0df7a6b00ce498f92eb5e1cfd6a2d87d8ebd5364017d1ce2f0839bdbf1bad7ae37f845e7fe2116e0c1197536bfbad549f3876c3c590000013e2598f743726006b8de42476ed56a55a75629a7b82e430c4e7c101a69e9b02a011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e0000000000", + "orchardTree": "012ff8ec2da5684e75195ca3be148c2d1fdab647848993c57a0094ae56b3f8aa1a001f00016be1e58474aa3149c3134be705a323141bba08d15fda001449ee24d1473e55380175414578d3f0c6104759ba284786e7f647214afeced76161f22dca0fb0671908015bc02f397a949f4195c09d547f22b1c5243d43cd6d7187f959f1aee7ff69de0c0197ef4dc418b370caff537b1557da0a466ff9440c0109f1f248f56cbec7c78b16010eb8701f4fb780766f8dc8050fc47a1529ec1f6ea4ac2fe4191fdac7ff91041c00013a71bdcbf164f8fa61cf6a3a057167fb91fcc9296df6222d5d010c248dc7f13501bc080d714f1761a7526c31badf0a5cc7d9b192f7f9380196fcc3ce2146f71b13000188c7f21ce6fbdc7d161545b0050aa75aeeb0884849326b33180ca0365cb6210b00000140234bc97e83a54c01338f668afbf26a7e88d945d266bf2374ed25b051e8e92e01d2cadcf4fa18a265421bb67f40cdf518a208d55ea1cbaf7bdaaa12df99860d000001e4bb5b2b7df9f4c3f508da258d91255399df66e84af8795377b7696bb87df42300012829e8aacdf1501baaeb5cb6e189d4e7182228e3d4b9acf54713595241e97f21017c8ece2b2ab2355d809b58809b21c7a5e95cfc693cd689387f7533ec8749261e01cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3350000.json b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3350000.json new file mode 100644 index 0000000..6fe66bd --- /dev/null +++ b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3350000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "3350000", + "hash": "00000000007cf26cfa1ad66d6b09d49adb6b4cf18bf6409bfc621600c01504e1", + "time": 1779359129, + "saplingTree": "01fca7be66740db6352ed4499a6f4bb4508a6e46bc33f2d636cb19b362b21ba739001f0000000159e0fbf2bc549165950198a8fb11866defa168404dcb3b4f31d3e4d5a57e941a0000000157aff948d08dc586d69663d49816ebf59dc466cf4ba819903e865f39c41cf75500017d1a68c8d2a5fa9c517c8b24dd71ccbb332a7bf6e2518d71a253f88c4905584f012946f1f71e50fe21515671354c0447f30378066e0ed9c12664bae82f3cc77b1300000190e323f6e45d107d4c5cbe2c4cb4c18a466d71c43fb4b4613d3617ed45d8f456011d85b015e936981d3a869c16ed2e03aeeb2444206933acc3f7b384aeb963256601929f0d44045c58428bd8daca3e2c9ca5a0361b5573a4158251dbe9f63a84e94901931c48ea50688eae5e54c24d0df7a6b00ce498f92eb5e1cfd6a2d87d8ebd5364017d1ce2f0839bdbf1bad7ae37f845e7fe2116e0c1197536bfbad549f3876c3c590000013e2598f743726006b8de42476ed56a55a75629a7b82e430c4e7c101a69e9b02a011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e0000000000", + "orchardTree": "01984c9b721a04ec43f83af2fe55be4d5c35a3ffe4c5ef66f5c470f36656c30f01010355c689bb56ce852be016174c5dfb9dc318afeb7d073f65017ac66babac5e321f01307c9f8ee0f436765b99d2314a9d1bf9005bf7d43a57b29593efc2ee1d4e42120000000101a6c04f0de2a7683af45e5e89a71498c3f916da5857454f85835ae0765e5b1c01024543199efdffc0bf65e01742445be10f0e29a803117320f1465846d6a73a38000114e9dad761c617620e2222f7d0f13c21330fbdcb2fb6ad467b673cf5273c4b1b000001ddaf39259ef5e6d5b21d4213c1452be45e2f7af711bbb1176c3cf973a8c45e250164a1f5c861aea42e28991321d00dbb8ed5a0bf0a9e51a453b5766b10fabc5f2a01d4607d9c3a44b2a0e4a25a4852a897c1da40bd8e168183ba8177407a452d9d180000012058ed30845b642624f5e97c395da919472fc4cf33310e9c6c5592fb2402863a01e4bb5b2b7df9f4c3f508da258d91255399df66e84af8795377b7696bb87df42300012829e8aacdf1501baaeb5cb6e189d4e7182228e3d4b9acf54713595241e97f21017c8ece2b2ab2355d809b58809b21c7a5e95cfc693cd689387f7533ec8749261e01cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3360000.json b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3360000.json new file mode 100644 index 0000000..e7c9dcc --- /dev/null +++ b/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/3360000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "3360000", + "hash": "0000000000c978c3e17f5730423403a1cbbd5ed0760438bdc42ccb2691d704d9", + "time": 1780112204, + "saplingTree": "016f5a3e5e7378449a233939c7dbbc2b360b12ccce85aca6d590e60b394384ed33001f000001e04d2e260a8164be37778f82836e58853b85fdc1d974ce74c131971cea258b1c01af4546de2dfd1e537fcee95c997788264757f469b54af747acd470de5afeb6630135f26225cd71bbfba4da9f92dafab83ba4f9a5ca584d56173e55951328b5c90301bc0e7ec13ff3ccb3179dfcaeb79ef2135158895d49fc7066e19152b2dc8a182a000000015a6f7dddd2c4a83a3959d83ea432d804fb61beee384eca3ae6d95a3a2650282c013a34a53f2da9c6ad7ee7fdefc7e53b47581308409bb831d2d33476835ce7f7500108e503d54848e8fa94b9e24529ba433446f05f9cb5ca52cf046465b24dad943e000190e323f6e45d107d4c5cbe2c4cb4c18a466d71c43fb4b4613d3617ed45d8f456011d85b015e936981d3a869c16ed2e03aeeb2444206933acc3f7b384aeb963256601929f0d44045c58428bd8daca3e2c9ca5a0361b5573a4158251dbe9f63a84e94901931c48ea50688eae5e54c24d0df7a6b00ce498f92eb5e1cfd6a2d87d8ebd5364017d1ce2f0839bdbf1bad7ae37f845e7fe2116e0c1197536bfbad549f3876c3c590000013e2598f743726006b8de42476ed56a55a75629a7b82e430c4e7c101a69e9b02a011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e0000000000", + "orchardTree": "01adc0dc4968e89db5f2f59dc442c579a0eefd947c71edc2f2d7b9d3d1da3c010e01d961228e10f26de0d28a0ae3f84d0a883fe548af6146cb0e3e222de6a718b6001f01088f91a6f1e17300f8696b16b725784e5172dec953241523b669d648c78dcd1000000000011a51c0577b39dec68c9cadf4b6630031df4103d8ba0cb212896b05133bd0c70c0148f336981d1721588868aec819dcad5115b5b96efd8e45ebd5184b64e11b8a0f0001bb9c9de1d49f42f051e51acedf6aca375ddedc18ead68e6f131052d03861d7100000014a11b178cdc85ff3a87fe310311658b95fa742358e14522236093ac1ecc2cb1b0170c3f32332a37e8699942bef527ab282a6d8ecb0fc1eb73f92528aa528f6e5250001d524b4a2c2d3cc4940420cd4c854db574ecb92ae2457d5879dde5c2965615336012058ed30845b642624f5e97c395da919472fc4cf33310e9c6c5592fb2402863a01e4bb5b2b7df9f4c3f508da258d91255399df66e84af8795377b7696bb87df42300012829e8aacdf1501baaeb5cb6e189d4e7182228e3d4b9acf54713595241e97f21017c8ece2b2ab2355d809b58809b21c7a5e95cfc693cd689387f7533ec8749261e01cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt b/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt index 15330b8..d20b26b 100644 --- a/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt +++ b/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt @@ -82,19 +82,30 @@ class RNZcashModule( } val wallet = getWallet(alias) val scope = wallet.coroutineScope - combine(wallet.progress, wallet.networkHeight) { progress, networkHeight -> - return@combine mapOf("progress" to progress, "networkHeight" to networkHeight) + // Synchronizer.progress now blends scan + recovery and never hits 100%, so + // read the un-blended per-wallet scan progress off the processor instead. + combine(wallet.processor.scanProgress, wallet.networkHeight, wallet.status) { scanProgress, networkHeight, status -> + return@combine mapOf("scanProgress" to scanProgress, "networkHeight" to networkHeight, "status" to status) }.collectWith(scope) { map -> - val progress = map["progress"] as PercentDecimal + val scanProgressDecimal = map["scanProgress"] as PercentDecimal + val status = map["status"] as Synchronizer.Status var networkBlockHeight = map["networkHeight"] as BlockHeight? if (networkBlockHeight == null) networkBlockHeight = BlockHeight.new(birthdayHeight.toLong()) + // Report scan progress as a 0-100 percentage but keep the decimal places + // (no truncation) so consumers get granular, more frequent updates. Force + // 100.0 when SYNCED, and 0.0 when not actively syncing (stopped / + // disconnected / initializing) instead of reusing a stale percentage, to + // match the iOS module. + val scanProgress = when (status) { + Synchronizer.Status.SYNCED -> 100.0 + Synchronizer.Status.SYNCING -> scanProgressDecimal.decimal.toDouble() * 100 + else -> 0.0 + } + sendEvent("UpdateEvent") { args -> args.putString("alias", alias) - args.putInt( - "scanProgress", - progress.toPercentage(), - ) + args.putDouble("scanProgress", scanProgress) args.putInt("networkBlockHeight", networkBlockHeight.value.toInt()) } } @@ -200,10 +211,6 @@ class RNZcashModule( handleError("error", error) false } - wallet.onSubmissionErrorHandler = { error -> - handleError("error", error) - false - } wallet.onChainErrorHandler = { errorHeight, rewindHeight -> val message = "Chain error detected at height: $errorHeight. Rewinding to: $rewindHeight" handleError("error", Throwable(message)) diff --git a/ios/RNZcash.swift b/ios/RNZcash.swift index 0286ea8..7783053 100644 --- a/ios/RNZcash.swift +++ b/ios/RNZcash.swift @@ -54,7 +54,7 @@ struct ConfirmedTx { } struct ProcessorState { - var scanProgress: Int + var scanProgress: Double var networkBlockHeight: Int var dictionary: [String: Any] { return [ @@ -641,15 +641,17 @@ class WalletSynchronizer: NSObject { func updateProcessorState(event: SynchronizerState) { updateBalanceState(event: event) - var scanProgress = 0 + var scanProgress = 0.0 switch event.internalSyncStatus { case .syncing(let progress, _): - scanProgress = Int(floor(progress * 100)) + // Report a 0-100 percentage but keep the decimal places (no floor) for + // granular progress. + scanProgress = Double(progress) * 100 case .synced: - scanProgress = 100 + scanProgress = 100.0 case .unprepared, .disconnected, .stopped: - scanProgress = 0 + scanProgress = 0.0 default: return } diff --git a/scripts/updateSources.ts b/scripts/updateSources.ts index dd0b301..92249df 100644 --- a/scripts/updateSources.ts +++ b/scripts/updateSources.ts @@ -4,8 +4,9 @@ // and install it into the correct locations. import { execFileSync } from 'child_process' +import { createHash } from 'crypto' import { deepList, justFiles, makeNodeDisklet, navigateDisklet } from 'disklet' -import { existsSync, mkdirSync } from 'fs' +import { existsSync, mkdirSync, readFileSync } from 'fs' import { join } from 'path' import { copyCheckpoints } from './copyCheckpoints' @@ -21,31 +22,63 @@ async function main(): Promise { await copyCheckpoints(disklet) } +// The Swift SDK version to vendor. The matching libzcashlc.xcframework is +// downloaded from this release's assets (see rebuildXcframework). +const ZCASH_SWIFT_SDK_VERSION = '2.5.2' + +// SHA-256 of the libzcashlc.xcframework.zip release asset for the version +// above. The download is verified against this pin before it is unpacked, so a +// tampered or swapped upstream asset fails the build instead of injecting +// attacker-controlled native code. Update this whenever the SDK version bumps: +// curl -fL https://github.com/zcash/zcash-swift-wallet-sdk/releases/download//libzcashlc.xcframework.zip | shasum -a 256 +const LIBZCASHLC_XCFRAMEWORK_SHA256 = + '27089796e15eacd0e5a90e7ea01884ea5c40806cf25a6fa9a6aca933dad65813' + function downloadSources(): void { getRepo( 'ZcashLightClientKit', - 'https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk.git', - // 2.4.0: - '1cf8a2375264995224f8282eaf63931439c28368' - ) - getRepo( - 'zcash-light-client-ffi', - 'https://github.com/Electric-Coin-Company/zcash-light-client-ffi.git', - // 0.19.0: - 'a7211faa2cea15db017fa138043ba712f61724a2' + 'https://github.com/zcash/zcash-swift-wallet-sdk.git', + // 2.5.2: + 'e725a2482dced83afda91bcebe881bd0791aa359' ) + // libzcashlc is no longer a separate package as of SDK 2.5.x — it ships as a + // binaryTarget zip on the SDK's GitHub release, downloaded in rebuildXcframework(). } /** - * Re-packages zcash-light-client-ffi. + * Downloads and re-packages the libzcashlc XCFramework. + * + * As of SDK 2.5.x the FFI ships as a release-asset zip on the Swift SDK repo + * (no longer a separate zcash-light-client-ffi package). * * An XCFramework can either include a static library (.a) * or a dynamically-linked library (.framework). - * The zcash-light-client-ffi package tries to stuff a static library - * into a dynamic framework, which doesn't work correctly. + * The published XCFramework stuffs a static library into a dynamic framework, + * which doesn't work correctly. * We fix this by simply re-building the XCFramework. */ async function rebuildXcframework(): Promise { + // Download the prebuilt libzcashlc XCFramework from the SDK's GitHub release. + // (The SDK's Package.swift `.binaryTarget` points at this same asset.) + console.log('Downloading libzcashlc XCFramework...') + const zipUrl = `https://github.com/zcash/zcash-swift-wallet-sdk/releases/download/${ZCASH_SWIFT_SDK_VERSION}/libzcashlc.xcframework.zip` + const zipPath = join(tmp, 'libzcashlc.xcframework.zip') + loudExec(tmp, ['curl', '--fail', '--location', '--output', zipPath, zipUrl]) + + // Verify the downloaded asset against the pinned SHA-256 before unpacking it, + // so a tampered/replaced upstream release can't inject native code into the build. + const actualSha256 = createHash('sha256') + .update(readFileSync(zipPath)) + .digest('hex') + if (actualSha256 !== LIBZCASHLC_XCFRAMEWORK_SHA256) { + throw new Error( + `libzcashlc.xcframework.zip integrity check failed: expected ${LIBZCASHLC_XCFRAMEWORK_SHA256}, got ${actualSha256}` + ) + } + + await disklet.delete('tmp/libzcashlc.xcframework') + loudExec(tmp, ['unzip', '-q', '-o', zipPath]) + console.log('Creating XCFramework...') await disklet.delete('ios/libzcashlc.xcframework') @@ -53,13 +86,13 @@ async function rebuildXcframework(): Promise { await disklet.setData( 'tmp/lib/ios-simulator/libzcashlc.a', await disklet.getData( - 'tmp/zcash-light-client-ffi/releases/XCFramework/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.framework/libzcashlc' + 'tmp/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.framework/libzcashlc' ) ) await disklet.setData( 'tmp/lib/ios/libzcashlc.a', await disklet.getData( - 'tmp/zcash-light-client-ffi/releases/XCFramework/libzcashlc.xcframework/ios-arm64/libzcashlc.framework/libzcashlc' + 'tmp/libzcashlc.xcframework/ios-arm64/libzcashlc.framework/libzcashlc' ) ) @@ -149,7 +182,7 @@ async function copySwift(): Promise { await disklet.setText( 'ios/zcashlc.h', await disklet.getText( - 'tmp/zcash-light-client-ffi/releases/XCFramework/libzcashlc.xcframework/ios-arm64/libzcashlc.framework/Headers/zcashlc.h' + 'tmp/libzcashlc.xcframework/ios-arm64/libzcashlc.framework/Headers/zcashlc.h' ) ) } diff --git a/src/types.ts b/src/types.ts index 9affcf1..a33a50f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -71,7 +71,7 @@ export interface TransactionEvent { export interface UpdateEvent { alias: string - scanProgress: number // 0 - 100 + scanProgress: number // 0 - 100 (may include decimal places) networkBlockHeight: number }