diff options
103 files changed, 5805 insertions, 4467 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 1f60099c94..9883f87c16 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -168,8 +168,11 @@ task: << : *GLOBAL_TASK_TEMPLATE container: image: ubuntu:focal + cpu: 4 + memory: 16G # The default memory is sometimes just a bit too small, so double everything env: << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV + MAKEJOBS: "-j8" FILE_ENV: "./ci/test/00_setup_env_native_multiprocess.sh" task: diff --git a/configure.ac b/configure.ac index 2bc404250d..505468a8bb 100644 --- a/configure.ac +++ b/configure.ac @@ -888,7 +888,7 @@ if test x$use_hardening != xno; then dnl See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458 for more details. ;; *) - AX_CHECK_COMPILE_FLAG([-fstack-clash-protection],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-clash-protection"]) + AX_CHECK_COMPILE_FLAG([-fstack-clash-protection], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-clash-protection"], [], [$CXXFLAG_WERROR]) ;; esac diff --git a/contrib/seeds/nodes_test.txt b/contrib/seeds/nodes_test.txt index 0af88d1bde..118bec280e 100644 --- a/contrib/seeds/nodes_test.txt +++ b/contrib/seeds/nodes_test.txt @@ -1,11 +1,16 @@ # List of fixed seed nodes for testnet # Onion nodes -thfsmmn2jbitcoin.onion:18333 -it2pj4f7657g3rhi.onion:18333 -nkf5e6b7pl4jfd4a.onion:18333 -4zhkir2ofl7orfom.onion:18333 -t6xj6wilh4ytvcs7.onion:18333 -i6y6ivorwakd7nw3.onion:18333 -ubqj4rsu3nqtxmtp.onion:18333 +35k2va6vyw4oo5ly2quvcszgdqr56kcnfgcqpnpcffut4jn3mhhwgbid.onion:18333 +blo2esfvk2rr7sr4jspmu3vt2vpgr5rigflsj645fnku7v4qmljurtid.onion:18333 +fuckcswupr5rmlvx2kqqrrosxvjyong4hatmuvxsvtcwe4dsh5rus7qd.onion:18333 +gblylyacjlitd2ywdmo2qqylwtdky7kgeqfvlhiw4zdag4x62tx54hyd.onion:18333 +gzwpduv33l7yze3bcdzj3inebiyjwddjnwvnjhh5wvnv4me76mjt2kad.onion:18333 +h3rphzofxzq52tb63mg5f6kc4my3fkcrgh3m5qryeatts43iljbawiid.onion:18333 +kf4qlhek34b3kgyxyodlmvgm4bxfrjsbjtgayyaiuyhr2eoyfgtm3bad.onion:18333 +mc7k47ndjvvhcgs54wmjzxvate4rtuybbjoryikdssjhcxlx27psbyqd.onion:18333 +mrhiniicugfo7mgrwv3wtolk3tptlcw2uq7ih6sq43fa4k4zbilut3yd.onion:18333 +uiudyws3qizgmepfoh7wwjmsoxoxut4qrmotjjhrn247xnjopr7sfcid.onion:18333 +zc2wvoqcezcrf64trji6jmhtss34a5ds5ntzdhqegzvex3ynrd7nxcad.onion:18333 +zd5m3dgdn46naj36pxvvcalfw2paecle6sdxq64ptwxtxjomkywpklqd.onion:18333 diff --git a/contrib/windeploy/win-codesign.cert b/contrib/windeploy/win-codesign.cert index 4023a5b638..e763df5847 100644 --- a/contrib/windeploy/win-codesign.cert +++ b/contrib/windeploy/win-codesign.cert @@ -1,100 +1,89 @@ -----BEGIN CERTIFICATE----- -MIIFdDCCBFygAwIBAgIRAL98pqZb/N9LuNaNxKsHNGQwDQYJKoZIhvcNAQELBQAw -fDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQD -ExtTZWN0aWdvIFJTQSBDb2RlIFNpZ25pbmcgQ0EwHhcNMjAwMzI0MDAwMDAwWhcN -MjEwMzI0MjM1OTU5WjCBtzELMAkGA1UEBhMCQ0gxDTALBgNVBBEMBDgwMDUxDjAM -BgNVBAgMBVN0YXRlMRAwDgYDVQQHDAdaw7xyaWNoMRcwFQYDVQQJDA5NYXR0ZW5n -YXNzZSAyNzEuMCwGA1UECgwlQml0Y29pbiBDb3JlIENvZGUgU2lnbmluZyBBc3Nv -Y2lhdGlvbjEuMCwGA1UEAwwlQml0Y29pbiBDb3JlIENvZGUgU2lnbmluZyBBc3Nv -Y2lhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMtxC8N4r/jE -OGOdFy/0UtiUvEczPZf9WYZz/7paAkc75XopHIE5/ssmoEX27gG9K00tf3Q62QAx -inZUPWkNTh8X0l+6uSGiIBFIV7dDgztIxnPcxaqw0k7Q2TEqKJvb5qm16zX6WfXJ -R2r6O5utUdQ3AarHnQq9fwdM1j5+ywS5u52te74ENgDMTMKUuB2J3KH1ASg5PAtO -CjPqPL+ZXJ7eT3M0Z+Lbu5ISZSqZB48BcCwOo/fOO0dAiLT9FE1iVtaCpBKHqGmd -glRjPzZdgDv8g28etRmk8wQ5pQmfL2gBjt/LtIgMPTdHHETKLxJO5H3y0CNx1vzL -ql7xNMxELxkCAwEAAaOCAbMwggGvMB8GA1UdIwQYMBaAFA7hOqhTOjHVir7Bu61n -GgOFrTQOMB0GA1UdDgQWBBSHBbl82FUJiUkXyyYJog1awYRsxjAOBgNVHQ8BAf8E -BAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgB -hvhCAQEEBAMCBBAwQAYDVR0gBDkwNzA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEF -BQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwQwYDVR0fBDwwOjA4oDagNIYy -aHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNBQ29kZVNpZ25pbmdDQS5j -cmwwcwYIKwYBBQUHAQEEZzBlMD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LnNlY3Rp -Z28uY29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNydDAjBggrBgEFBQcwAYYX -aHR0cDovL29jc3Auc2VjdGlnby5jb20wKwYDVR0RBCQwIoEgam9uYXNAYml0Y29p -bmNvcmVjb2Rlc2lnbmluZy5vcmcwDQYJKoZIhvcNAQELBQADggEBAAU59qJzQ2ED -aTMIQTsU01zIhZJ/xwQh78i0v2Mnr46RvzYrZOev+btF3SyUYD8veNnbYlY6yEYq -Vb+/PQnE3t1xlqR80qiTZCk/Wmxx/qKvQuWeRL5QQgvsCmWBpycQ7PNfwzOWxbPE -b0Hb2/VFFZfR9iltkfeInRUrzS96CJGYtm7dMf2JtnXYBcwpn1N8BSMH4nXVyN8g -VEE5KyjE7+/awYiSST7+e6Y7FE5AJ4f3FjqnRm+2XetTVqITwMLKZMoV283nSEeH -fA4FNAMGz9QeV38ol65NNqFP2vSSgVoPK79orqH9OOW2LSobt2qun+euddJIQeYV -CMP90b/2WPc= +MIIGQzCCBSugAwIBAgIQBSN7Cm16Z0UT9p7lA2jiKDANBgkqhkiG9w0BAQsFADBy +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQg +SUQgQ29kZSBTaWduaW5nIENBMB4XDTIxMDUyMTAwMDAwMFoXDTIyMDUyNjIzNTk1 +OVowgYAxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhEZWxhd2FyZTEOMAwGA1UEBxMF +TGV3ZXMxJjAkBgNVBAoTHUJpdGNvaW4gQ29yZSBDb2RlIFNpZ25pbmcgTExDMSYw +JAYDVQQDEx1CaXRjb2luIENvcmUgQ29kZSBTaWduaW5nIExMQzCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAKe6xtFgKAQ68MvxwCjNtpgPobfDQCLKvCAN +uBKGYuub6ufQB5dhCLN9fjMgfg33AyauvU3PcEUDUWD3/k925bPqgxHC3E7YqoB+ +11b/2Y7a86okqUgcGgvKhaKoHmXxElpM9EjQHjJ0yL4QAR1Lp+9CMMW3wIulBYKt +wLIArFvbuQhMO/6rxL8frpK049v//WfQzB16GXuFnzN/6fDK7oOt5IrKTg4H6EY2 +fj4+QaUj0lNX7aHnZ6Ki45h2RUPDgN1ipRIuhM67npyZ/tdzPPjI3PUgfXCccN6D ++qWWnbbbvPuOht4ziPciVnPd57PqJmAOnLI86gisDfd7VKlcpOSEaagdUGvMbU6f +uAps818GwnJzwCGllxlKASCgXDAckLLvMuit4RfYAhhdhw5R0AsaWK0HW88oHOqi +U7eWlMCbSGk34x9hBrxYl7tvcNcLPWIPYrrhFWNFpkV8bVVIoV5rUNRgWvBcdOq1 +CCPTfsJp3nEH2WCoBghZquDZLSW12wMw2UsQyEojBeGhrR1inn8uK93wSnVCC8F4 +21yWNRMNe/LQVhmZDgFOen9r/WijBsBdQw1bL8N4zGdYv8+soqkrWzW417FfSx81 +pj4j5FEXYXXV5k/4/eBpIARXVRR8xya0nGkhNJmBk0jjDGD8fPW2gFQbqnUwAQ34 +vOr8NUqHAgMBAAGjggHEMIIBwDAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg+S32 +ZXUOWDAdBgNVHQ4EFgQUVSLtZnifEHvd8z3E7AyLYNuDiaMwDgYDVR0PAQH/BAQD +AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6 +Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Ax +hi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNy +bDBLBgNVHSAERDBCMDYGCWCGSAGG/WwDATApMCcGCCsGAQUFBwIBFhtodHRwOi8v +d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYw +JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcw +AoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3Vy +ZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL +BQADggEBAOaJneI91NJgqghUxgc0AWQ01SAJTgN4z7xMQ3W0ZAtwGbA0byT7YRlj +j7h+j+hMX/JYkRJETTh8Nalq2tPWJBiMMEPOGFVttFER1pwouHkK9pSKyp4xRvNU +L0LPh7fE4EYMJoynys6ZTpMCHLku+X3jFat1+1moh9TJRvK5+ETZYGl0seFNU3mJ +dZzusObm4scffIGgi40kmmISKd5ZRuooRTu9FFR/3vpfbA+7Vg4RSH3CcQPo9bfk ++h/qRQhSfQInTBn7obRpIlvEcK782qivqseJGdtnTmcdVRShD5ckTVza1yv25uQz +l/yTqmG2LXlYjl5iMSdF0C1xYq6IsOA= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw -MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B -3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY -tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ -Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 -VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT -79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 -c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT -Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l -c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee -UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE -Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd -BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G -A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF -Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO -VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 -ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs -8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR -iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze -Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ -XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ -qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB -VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB -L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG -jjxDah2nGN59PRbxYvnKkKj9 +MIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBT +aWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+NOzHH8O +Ea9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ1DcZ17aq +8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0sSgmuyRp +wsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6scKKrzn/p +fMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4TzrGdOtcT3 +jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg0A9kczye +n6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV +HQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUHAQEEbTBr +MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUH +MAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ +RFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2lj +ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6 +Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmww +TwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v +d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYEFFrEuXsq +CqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgP +MA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06GsTvMGHX +fgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5jDhNLrddf +RHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgCPC6Ro8Al +EeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIysjaKJAL+ +L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4GbT8aTEAb8 +B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHv -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIF9TCCA92gAwIBAgIQHaJIMG+bJhjQguCWfTPTajANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx -MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjB8MQswCQYDVQQGEwJHQjEbMBkGA1UE -CBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRgwFgYDVQQK -Ew9TZWN0aWdvIExpbWl0ZWQxJDAiBgNVBAMTG1NlY3RpZ28gUlNBIENvZGUgU2ln -bmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIYijTKFehif -SfCWL2MIHi3cfJ8Uz+MmtiVmKUCGVEZ0MWLFEO2yhyemmcuVMMBW9aR1xqkOUGKl -UZEQauBLYq798PgYrKf/7i4zIPoMGYmobHutAMNhodxpZW0fbieW15dRhqb0J+V8 -aouVHltg1X7XFpKcAC9o95ftanK+ODtj3o+/bkxBXRIgCFnoOc2P0tbPBrRXBbZO -oT5Xax+YvMRi1hsLjcdmG0qfnYHEckC14l/vC0X/o84Xpi1VsLewvFRqnbyNVlPG -8Lp5UEks9wO5/i9lNfIi6iwHr0bZ+UYc3Ix8cSjz/qfGFN1VkW6KEQ3fBiSVfQ+n -oXw62oY1YdMCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvA -nfKyA2bLMB0GA1UdDgQWBBQO4TqoUzox1Yq+wbutZxoDha00DjAOBgNVHQ8BAf8E -BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEFBQcDAwYI -KwYBBQUHAwgwEQYDVR0gBAowCDAGBgRVHSAAMFAGA1UdHwRJMEcwRaBDoEGGP2h0 -dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9u -QXV0aG9yaXR5LmNybDB2BggrBgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6 -Ly9jcnQudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAl -BggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0B -AQwFAAOCAgEATWNQ7Uc0SmGk295qKoyb8QAAHh1iezrXMsL2s+Bjs/thAIiaG20Q -BwRPvrjqiXgi6w9G7PNGXkBGiRL0C3danCpBOvzW9Ovn9xWVM8Ohgyi33i/klPeF -M4MtSkBIv5rCT0qxjyT0s4E307dksKYjalloUkJf/wTr4XRleQj1qZPea3FAmZa6 -ePG5yOLDCBaxq2NayBWAbXReSnV+pbjDbLXP30p5h1zHQE1jNfYw08+1Cg4LBH+g -S667o6XQhACTPlNdNKUANWlsvp8gJRANGftQkGG+OY96jk32nw4e/gdREmaDJhlI -lc5KycF/8zoFm/lv34h/wCOe0h5DekUxwZxNqfBZslkZ6GqNKQQCd3xLS81wvjqy -VVp4Pry7bwMQJXcVNIr5NsxDkuS6T/FikyglVyn7URnHoSVAaoRXxrKdsbwcCtp8 -Z359LukoTBh+xHsxQXGaSynsCz1XUNLK3f2eBVHlRHjdAd6xdZgNVCT98E7j4viD -vXK6yz067vBeF5Jobchh+abxKgoLpbn0nu6YMgWFnuv5gynTxix9vTp3Los3QqBq -gu07SqqUEKThDfgXxbZaeTMYkuO1dfih6Y4KJR7kHvGfWocj/5+kUZ77OYARzdu1 -xKeogG/lU9Tg46LC0lsa+jImLWpXcBw8pFguo/NbSwfcMlnzh6cabVg= +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== -----END CERTIFICATE----- + diff --git a/depends/config.guess b/depends/config.guess index 7f9ebbe310..dc0a6b2997 100755 --- a/depends/config.guess +++ b/depends/config.guess @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2019 Free Software Foundation, Inc. +# Copyright 1992-2021 Free Software Foundation, Inc. -timestamp='2019-09-10' +timestamp='2021-05-24' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -27,12 +27,12 @@ timestamp='2019-09-10' # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to <config-patches@gnu.org>. -me=`echo "$0" | sed -e 's,.*/,,'` +me=$(echo "$0" | sed -e 's,.*/,,') usage="\ Usage: $0 [OPTION] @@ -50,7 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2019 Free Software Foundation, Inc. +Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -99,9 +99,11 @@ tmp= trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039 - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } @@ -129,16 +131,14 @@ if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown +UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown +UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown +UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown -case "$UNAME_SYSTEM" in +case $UNAME_SYSTEM in Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu + LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" @@ -147,24 +147,36 @@ Linux|GNU|GNU/*) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc - #else + #elif defined(__GLIBC__) LIBC=gnu + #else + #include <stdarg.h> + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif #endif EOF - eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')" - # If ldd exists, use it to detect musl libc. - if command -v ldd >/dev/null && \ - ldd --version 2>&1 | grep -q ^musl - then - LIBC=musl + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. -case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -176,27 +188,27 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - "/sbin/$sysctl" 2>/dev/null || \ - "/usr/sbin/$sysctl" 2>/dev/null || \ - echo unknown)` - case "$UNAME_MACHINE_ARCH" in + UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + echo unknown)) + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) - arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` - endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,') + endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p') machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. - case "$UNAME_MACHINE_ARCH" in + case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; @@ -217,10 +229,10 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in ;; esac # Determine ABI tags. - case "$UNAME_MACHINE_ARCH" in + case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr") ;; esac # The OS release @@ -228,12 +240,12 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "$UNAME_VERSION" in + case $UNAME_VERSION in Debian*) release='-gnu' ;; *) - release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2) ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: @@ -242,15 +254,19 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in echo "$machine-${os}${release}${abi-}" exit ;; *:Bitrig:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//') echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//') echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=$(arch | sed 's/SecBSD.//') + echo "$UNAME_MACHINE_ARCH"-unknown-secbsd"$UNAME_RELEASE" + exit ;; *:LibertyBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//') echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) @@ -284,20 +300,22 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 case $UNAME_RELEASE in *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}') ;; *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}') ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in + ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1) + case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") @@ -334,11 +352,8 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; + echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)" + exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; @@ -368,7 +383,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then + if test "$( (/bin/universe) 2>/dev/null)" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd @@ -381,17 +396,17 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in + case $(/usr/bin/uname -p) in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) - echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" exit ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" @@ -402,7 +417,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null @@ -410,30 +425,30 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in SUN_ARCH=x86_64 fi fi - echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in + case $(/usr/bin/arch -k) in Series*|S4*) - UNAME_RELEASE=`uname -v` + UNAME_RELEASE=$(uname -v) ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" + echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null) test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 - case "`/bin/arch`" in + case $(/bin/arch) in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; @@ -513,8 +528,8 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && - dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`"$dummy" "$dummyarg"` && + dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') && + SYSTEM_NAME=$("$dummy" "$dummyarg") && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; @@ -541,11 +556,11 @@ EOF exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] + UNAME_PROCESSOR=$(/usr/bin/uname -p) + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then - if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ - [ "$TARGET_BINARY_INTERFACE"x = x ] + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x then echo m88k-dg-dgux"$UNAME_RELEASE" else @@ -569,17 +584,17 @@ EOF echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) - echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" + echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if test -x /usr/bin/oslevel ; then + IBM_REV=$(/usr/bin/oslevel) else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi @@ -599,7 +614,7 @@ EOF exit(0); } EOF - if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") then echo "$SYSTEM_NAME" else @@ -612,15 +627,15 @@ EOF fi exit ;; *:AIX:*:[4567]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }') if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if [ -x /usr/bin/lslpp ] ; then - IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | - awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + if test -x /usr/bin/lslpp ; then + IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/) else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi @@ -648,26 +663,26 @@ EOF echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` - case "$UNAME_MACHINE" in + HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') + case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "$sc_cpu_version" in + if test -x /usr/bin/getconf; then + sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null) + sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null) + case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "$sc_kernel_bits" in + case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "$HP_ARCH" = "" ]; then + if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" @@ -702,11 +717,11 @@ EOF exit (0); } EOF - (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy") test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ "$HP_ARCH" = hppa2.0w ] + if test "$HP_ARCH" = hppa2.0w then set_cc_for_build @@ -730,7 +745,7 @@ EOF echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) - HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) @@ -760,7 +775,7 @@ EOF exit (0); } EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; @@ -780,7 +795,7 @@ EOF echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then + if test -x /usr/sbin/sysversion ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 @@ -829,14 +844,14 @@ EOF echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz) + FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') + FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/') echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') + FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/') echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) @@ -849,25 +864,25 @@ EOF echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; arm:FreeBSD:*:*) - UNAME_PROCESSOR=`uname -p` + UNAME_PROCESSOR=$(uname -p) set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi + echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi else - echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf + echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf fi exit ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case "$UNAME_PROCESSOR" in + UNAME_PROCESSOR=$(/usr/bin/uname -p) + case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac - echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin @@ -885,7 +900,7 @@ EOF echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) - case "$UNAME_MACHINE" in + case $UNAME_MACHINE in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; @@ -903,15 +918,15 @@ EOF echo x86_64-pc-cygwin exit ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" exit ;; *:GNU:*:*) # the GNU system - echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" + echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" + echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC" exit ;; *:Minix:*:*) echo "$UNAME_MACHINE"-unknown-minix @@ -924,7 +939,7 @@ EOF echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -937,7 +952,7 @@ EOF if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; - arc:Linux:*:* | arceb:Linux:*:*) + arc:Linux:*:* | arceb:Linux:*:* | arc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) @@ -983,6 +998,9 @@ EOF k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; @@ -1033,7 +1051,7 @@ EOF #endif #endif EOF - eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`" + eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) @@ -1053,7 +1071,7 @@ EOF exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; @@ -1071,7 +1089,7 @@ EOF ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; - riscv32:Linux:*:* | riscv64:Linux:*:*) + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) @@ -1093,7 +1111,17 @@ EOF echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) - echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + set_cc_for_build + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_X32 >/dev/null + then + LIBCABI="$LIBC"x32 + fi + fi + echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI" exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" @@ -1133,7 +1161,7 @@ EOF echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) - UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//') if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else @@ -1142,7 +1170,7 @@ EOF exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in + case $(/bin/uname -X | grep "^Machine") in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; @@ -1151,10 +1179,10 @@ EOF exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + UNAME_REL=$(sed -n 's/.*Version //p' </usr/options/cb.name) echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL" elif /bin/uname -X 2>/dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //')) (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 @@ -1204,7 +1232,7 @@ EOF 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ @@ -1215,7 +1243,7 @@ EOF NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ @@ -1248,7 +1276,7 @@ EOF exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` + UNAME_MACHINE=$( (uname -p) 2>/dev/null) echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv @@ -1282,7 +1310,7 @@ EOF echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then + if test -d /usr/nec; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" @@ -1330,8 +1358,11 @@ EOF *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; + arm64:Darwin:*:*) + echo aarch64-apple-darwin"$UNAME_RELEASE" + exit ;; *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` + UNAME_PROCESSOR=$(uname -p) case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac @@ -1344,7 +1375,7 @@ EOF else set_cc_for_build fi - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null @@ -1368,7 +1399,7 @@ EOF echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` + UNAME_PROCESSOR=$(uname -p) if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc @@ -1406,10 +1437,9 @@ EOF # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - # shellcheck disable=SC2154 - if test "$cputype" = 386; then + if test "${cputype-}" = 386; then UNAME_MACHINE=i386 - else + elif test "x${cputype-}" != x; then UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 @@ -1436,11 +1466,11 @@ EOF echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) - echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" exit ;; *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "$UNAME_MACHINE" in + UNAME_MACHINE=$( (uname -p) 2>/dev/null) + case $UNAME_MACHINE in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; @@ -1449,13 +1479,13 @@ EOF echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" + echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; - i*86:AROS:*:*) - echo "$UNAME_MACHINE"-pc-aros + *:AROS:*:*) + echo "$UNAME_MACHINE"-unknown-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx @@ -1507,7 +1537,7 @@ main () #define __ARCHITECTURE__ "m68k" #endif int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null); if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else @@ -1599,7 +1629,7 @@ main () } EOF -$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` && +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. @@ -1607,7 +1637,7 @@ test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 -case "$UNAME_MACHINE:$UNAME_SYSTEM" in +case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <<EOF @@ -1624,9 +1654,15 @@ This script (version $timestamp), has failed to recognize the operating system you are using. If your script is old, overwrite *all* copies of config.guess and config.sub with the latest versions from: - https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess + https://git.savannah.gnu.org/cgit/config.git/plain/config.guess and - https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + https://git.savannah.gnu.org/cgit/config.git/plain/config.sub +EOF + +year=$(echo $timestamp | sed 's,-.*,,') +# shellcheck disable=SC2003 +if test "$(expr "$(date +%Y)" - "$year")" -lt 3 ; then + cat >&2 <<EOF If $0 has already been updated, send the following data and any information you think might be pertinent to config-patches@gnu.org to @@ -1634,26 +1670,27 @@ provide the necessary information to handle your system. config.guess timestamp = $timestamp -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` +uname -m = $( (uname -m) 2>/dev/null || echo unknown) +uname -r = $( (uname -r) 2>/dev/null || echo unknown) +uname -s = $( (uname -s) 2>/dev/null || echo unknown) +uname -v = $( (uname -v) 2>/dev/null || echo unknown) -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` +/usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null) +/bin/uname -X = $( (/bin/uname -X) 2>/dev/null) -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` +hostinfo = $( (hostinfo) 2>/dev/null) +/bin/universe = $( (/bin/universe) 2>/dev/null) +/usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null) +/bin/arch = $( (/bin/arch) 2>/dev/null) +/usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null) +/usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null) UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF +fi exit 1 diff --git a/depends/config.sub b/depends/config.sub index a318a46868..7384e9198b 100755 --- a/depends/config.sub +++ b/depends/config.sub @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2019 Free Software Foundation, Inc. +# Copyright 1992-2021 Free Software Foundation, Inc. -timestamp='2019-06-30' +timestamp='2021-04-30' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ timestamp='2019-06-30' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -50,7 +50,7 @@ timestamp='2019-06-30' # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. -me=`echo "$0" | sed -e 's,.*/,,'` +me=$(echo "$0" | sed -e 's,.*/,,') usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS @@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) -Copyright 1992-2019 Free Software Foundation, Inc. +Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -124,28 +124,27 @@ case $1 in ;; *-*-*-*) basic_machine=$field1-$field2 - os=$field3-$field4 + basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \ - | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \ + nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 - os=$maybe_os + basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown - os=linux-android + basic_os=linux-android ;; *) basic_machine=$field1-$field2 - os=$field3 + basic_os=$field3 ;; esac ;; @@ -154,7 +153,7 @@ case $1 in case $field1-$field2 in decstation-3100) basic_machine=mips-dec - os= + basic_os= ;; *-*) # Second component is usually, but not always the OS @@ -162,7 +161,7 @@ case $1 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 - os=$field2 + basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ @@ -175,11 +174,11 @@ case $1 in | microblaze* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 - os= + basic_os= ;; *) basic_machine=$field1 - os=$field2 + basic_os=$field2 ;; esac ;; @@ -191,447 +190,451 @@ case $1 in case $field1 in 386bsd) basic_machine=i386-pc - os=bsd + basic_os=bsd ;; a29khif) basic_machine=a29k-amd - os=udi + basic_os=udi ;; adobe68k) basic_machine=m68010-adobe - os=scout + basic_os=scout ;; alliant) basic_machine=fx80-alliant - os= + basic_os= ;; altos | altos3068) basic_machine=m68k-altos - os= + basic_os= ;; am29k) basic_machine=a29k-none - os=bsd + basic_os=bsd ;; amdahl) basic_machine=580-amdahl - os=sysv + basic_os=sysv ;; amiga) basic_machine=m68k-unknown - os= + basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown - os=amigaos + basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown - os=sysv4 + basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo - os=sysv + basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo - os=bsd + basic_os=bsd ;; aros) basic_machine=i386-pc - os=aros + basic_os=aros ;; aux) basic_machine=m68k-apple - os=aux + basic_os=aux ;; balance) basic_machine=ns32k-sequent - os=dynix + basic_os=dynix ;; blackfin) basic_machine=bfin-unknown - os=linux + basic_os=linux ;; cegcc) basic_machine=arm-unknown - os=cegcc + basic_os=cegcc ;; convex-c1) basic_machine=c1-convex - os=bsd + basic_os=bsd ;; convex-c2) basic_machine=c2-convex - os=bsd + basic_os=bsd ;; convex-c32) basic_machine=c32-convex - os=bsd + basic_os=bsd ;; convex-c34) basic_machine=c34-convex - os=bsd + basic_os=bsd ;; convex-c38) basic_machine=c38-convex - os=bsd + basic_os=bsd ;; cray) basic_machine=j90-cray - os=unicos + basic_os=unicos ;; crds | unos) basic_machine=m68k-crds - os= + basic_os= ;; da30) basic_machine=m68k-da30 - os= + basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec - os= + basic_os= ;; delta88) basic_machine=m88k-motorola - os=sysv3 + basic_os=sysv3 ;; dicos) basic_machine=i686-pc - os=dicos + basic_os=dicos ;; djgpp) basic_machine=i586-pc - os=msdosdjgpp + basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd - os=ebmon + basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson - os=ose + basic_os=ose ;; gmicro) basic_machine=tron-gmicro - os=sysv + basic_os=sysv ;; go32) basic_machine=i386-pc - os=go32 + basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi - os=hms + basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi - os=xray + basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi - os=hms + basic_os=hms ;; harris) basic_machine=m88k-harris - os=sysv3 + basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp - os=hpux + basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp - os=bsd + basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp - os=osf + basic_os=osf ;; hppro) basic_machine=hppa1.1-hp - os=proelf + basic_os=proelf ;; i386mach) basic_machine=i386-mach - os=mach + basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi - os=sysv + basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown - os=linux + basic_os=linux ;; magnum | m3230) basic_machine=mips-mips - os=sysv + basic_os=sysv ;; merlin) basic_machine=ns32k-utek - os=sysv + basic_os=sysv ;; mingw64) basic_machine=x86_64-pc - os=mingw64 + basic_os=mingw64 ;; mingw32) basic_machine=i686-pc - os=mingw32 + basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown - os=mingw32ce + basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k - os=coff + basic_os=coff ;; morphos) basic_machine=powerpc-unknown - os=morphos + basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown - os=moxiebox + basic_os=moxiebox ;; msdos) basic_machine=i386-pc - os=msdos + basic_os=msdos ;; msys) basic_machine=i686-pc - os=msys + basic_os=msys ;; mvs) basic_machine=i370-ibm - os=mvs + basic_os=mvs ;; nacl) basic_machine=le32-unknown - os=nacl + basic_os=nacl ;; ncr3000) basic_machine=i486-ncr - os=sysv4 + basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc - os=netbsd + basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel - os=linux + basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony - os=newsos + basic_os=newsos ;; news1000) basic_machine=m68030-sony - os=newsos + basic_os=newsos ;; necv70) basic_machine=v70-nec - os=sysv + basic_os=sysv ;; nh3000) basic_machine=m68k-harris - os=cxux + basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris - os=cxux + basic_os=cxux ;; nindy960) basic_machine=i960-intel - os=nindy + basic_os=nindy ;; mon960) basic_machine=i960-intel - os=mon960 + basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq - os=nonstopux + basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm - os=os400 + basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson - os=ose + basic_os=ose ;; os68k) basic_machine=m68k-none - os=os68k + basic_os=os68k ;; paragon) basic_machine=i860-intel - os=osf + basic_os=osf ;; parisc) basic_machine=hppa-unknown - os=linux + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp ;; pw32) basic_machine=i586-unknown - os=pw32 + basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc - os=rdos + basic_os=rdos ;; rdos32) basic_machine=i386-pc - os=rdos + basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k - os=coff + basic_os=coff ;; sa29200) basic_machine=a29k-amd - os=udi + basic_os=udi ;; sei) basic_machine=mips-sei - os=seiux + basic_os=seiux ;; sequent) basic_machine=i386-sequent - os= + basic_os= ;; sps7) basic_machine=m68k-bull - os=sysv2 + basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem - os= + basic_os= ;; stratus) basic_machine=i860-stratus - os=sysv4 + basic_os=sysv4 ;; sun2) basic_machine=m68000-sun - os= + basic_os= ;; sun2os3) basic_machine=m68000-sun - os=sunos3 + basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun - os=sunos4 + basic_os=sunos4 ;; sun3) basic_machine=m68k-sun - os= + basic_os= ;; sun3os3) basic_machine=m68k-sun - os=sunos3 + basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun - os=sunos4 + basic_os=sunos4 ;; sun4) basic_machine=sparc-sun - os= + basic_os= ;; sun4os3) basic_machine=sparc-sun - os=sunos3 + basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun - os=sunos4 + basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun - os=solaris2 + basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun - os= + basic_os= ;; sv1) basic_machine=sv1-cray - os=unicos + basic_os=unicos ;; symmetry) basic_machine=i386-sequent - os=dynix + basic_os=dynix ;; t3e) basic_machine=alphaev5-cray - os=unicos + basic_os=unicos ;; t90) basic_machine=t90-cray - os=unicos + basic_os=unicos ;; toad1) basic_machine=pdp10-xkl - os=tops20 + basic_os=tops20 ;; tpf) basic_machine=s390x-ibm - os=tpf + basic_os=tpf ;; udi29k) basic_machine=a29k-amd - os=udi + basic_os=udi ;; ultra3) basic_machine=a29k-nyu - os=sym1 + basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec - os=none + basic_os=none ;; vaxv) basic_machine=vax-dec - os=sysv + basic_os=sysv ;; vms) basic_machine=vax-dec - os=vms + basic_os=vms ;; vsta) basic_machine=i386-pc - os=vsta + basic_os=vsta ;; vxworks960) basic_machine=i960-wrs - os=vxworks + basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs - os=vxworks + basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs - os=vxworks + basic_os=vxworks ;; xbox) basic_machine=i686-pc - os=mingw32 + basic_os=mingw32 ;; ymp) basic_machine=ymp-cray - os=unicos + basic_os=unicos ;; *) basic_machine=$1 - os= + basic_os= ;; esac ;; @@ -683,17 +686,17 @@ case $basic_machine in bluegene*) cpu=powerpc vendor=ibm - os=cnk + basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec - os=tops10 + basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec - os=tops20 + basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) @@ -703,7 +706,7 @@ case $basic_machine in dpx2*) cpu=m68k vendor=bull - os=sysv3 + basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k @@ -712,7 +715,7 @@ case $basic_machine in elxsi) cpu=elxsi vendor=elxsi - os=${os:-bsd} + basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 @@ -725,7 +728,7 @@ case $basic_machine in h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi - os=hiuxwe2 + basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 @@ -766,38 +769,38 @@ case $basic_machine in vendor=hp ;; i*86v32) - cpu=`echo "$1" | sed -e 's/86.*/86/'` + cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc - os=sysv32 + basic_os=sysv32 ;; i*86v4*) - cpu=`echo "$1" | sed -e 's/86.*/86/'` + cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc - os=sysv4 + basic_os=sysv4 ;; i*86v) - cpu=`echo "$1" | sed -e 's/86.*/86/'` + cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc - os=sysv + basic_os=sysv ;; i*86sol2) - cpu=`echo "$1" | sed -e 's/86.*/86/'` + cpu=$(echo "$1" | sed -e 's/86.*/86/') vendor=pc - os=solaris2 + basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray - os=${os:-unicos} + basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi - case $os in + case $basic_os in irix*) ;; *) - os=irix4 + basic_os=irix4 ;; esac ;; @@ -808,26 +811,26 @@ case $basic_machine in *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari - os=mint + basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony - os=newsos + basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next - case $os in + case $basic_os in openstep*) ;; nextstep*) ;; ns2*) - os=nextstep2 + basic_os=nextstep2 ;; *) - os=nextstep3 + basic_os=nextstep3 ;; esac ;; @@ -838,12 +841,12 @@ case $basic_machine in op50n-* | op60c-*) cpu=hppa1.1 vendor=oki - os=proelf + basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi - os=hiuxwe2 + basic_os=hiuxwe2 ;; pbd) cpu=sparc @@ -880,12 +883,12 @@ case $basic_machine in sde) cpu=mipsisa32 vendor=sde - os=${os:-elf} + basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs - os=vxworks + basic_os=vxworks ;; tower | tower-32) cpu=m68k @@ -902,7 +905,7 @@ case $basic_machine in w89k-*) cpu=hppa1.1 vendor=winbond - os=proelf + basic_os=proelf ;; none) cpu=none @@ -914,7 +917,7 @@ case $basic_machine in ;; leon-*|leon[3-9]-*) cpu=sparc - vendor=`echo "$basic_machine" | sed 's/-.*//'` + vendor=$(echo "$basic_machine" | sed 's/-.*//') ;; *-*) @@ -955,11 +958,11 @@ case $cpu-$vendor in # some cases the only manufacturer, in others, it is the most popular. craynv-unknown) vendor=cray - os=${os:-unicosmp} + basic_os=${basic_os:-unicosmp} ;; c90-unknown | c90-cray) vendor=cray - os=${os:-unicos} + basic_os=${Basic_os:-unicos} ;; fx80-unknown) vendor=alliant @@ -1003,7 +1006,7 @@ case $cpu-$vendor in dpx20-unknown | dpx20-bull) cpu=rs6000 vendor=bull - os=${os:-bosx} + basic_os=${basic_os:-bosx} ;; # Here we normalize CPU types irrespective of the vendor @@ -1012,7 +1015,7 @@ case $cpu-$vendor in ;; blackfin-*) cpu=bfin - os=linux + basic_os=linux ;; c54x-*) cpu=tic54x @@ -1025,7 +1028,7 @@ case $cpu-$vendor in ;; e500v[12]-*) cpu=powerpc - os=$os"spe" + basic_os=${basic_os}"spe" ;; mips3*-*) cpu=mips64 @@ -1035,7 +1038,7 @@ case $cpu-$vendor in ;; m68knommu-*) cpu=m68k - os=linux + basic_os=linux ;; m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*) cpu=s12z @@ -1045,7 +1048,7 @@ case $cpu-$vendor in ;; parisc-*) cpu=hppa - os=linux + basic_os=linux ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) cpu=i586 @@ -1081,7 +1084,7 @@ case $cpu-$vendor in cpu=mipsisa64sb1el ;; sh5e[lb]-*) - cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'` + cpu=$(echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/') ;; spur-*) cpu=spur @@ -1099,13 +1102,16 @@ case $cpu-$vendor in cpu=x86_64 ;; xscale-* | xscalee[bl]-*) - cpu=`echo "$cpu" | sed 's/^xscale/arm/'` + cpu=$(echo "$cpu" | sed 's/^xscale/arm/') + ;; + arm64-*) + cpu=aarch64 ;; # Recognize the canonical CPU Types that limit and/or modify the # company names they are paired with. cr16-*) - os=${os:-elf} + basic_os=${basic_os:-elf} ;; crisv32-* | etraxfs*-*) cpu=crisv32 @@ -1116,7 +1122,7 @@ case $cpu-$vendor in vendor=axis ;; crx-*) - os=${os:-elf} + basic_os=${basic_os:-elf} ;; neo-tandem) cpu=neo @@ -1138,16 +1144,12 @@ case $cpu-$vendor in cpu=nsx vendor=tandem ;; - s390-*) - cpu=s390 - vendor=ibm - ;; - s390x-*) - cpu=s390x - vendor=ibm + mipsallegrexel-sony) + cpu=mipsallegrexel + vendor=sony ;; tile*-*) - os=${os:-linux-gnu} + basic_os=${basic_os:-linux-gnu} ;; *) @@ -1163,8 +1165,8 @@ case $cpu-$vendor in | alphapca5[67] | alpha64pca5[67] \ | am33_2.0 \ | amdgcn \ - | arc | arceb \ - | arm | arm[lb]e | arme[lb] | armv* \ + | arc | arceb | arc64 \ + | arm | arm[lb]e | arme[lb] | armv* \ | avr | avr32 \ | asmjs \ | ba \ @@ -1183,6 +1185,7 @@ case $cpu-$vendor in | k1om \ | le32 | le64 \ | lm32 \ + | loongarch32 | loongarch64 | loongarchx32 \ | m32c | m32r | m32rle \ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ @@ -1201,9 +1204,13 @@ case $cpu-$vendor in | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r3 | mipsisa32r3el \ + | mipsisa32r5 | mipsisa32r5el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r3 | mipsisa64r3el \ + | mipsisa64r5 | mipsisa64r5el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ @@ -1227,8 +1234,9 @@ case $cpu-$vendor in | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ | pru \ | pyramid \ - | riscv | riscv32 | riscv64 \ + | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ | rl78 | romp | rs6000 | rx \ + | s390 | s390x \ | score \ | sh | shl \ | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ @@ -1238,6 +1246,7 @@ case $cpu-$vendor in | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ | spu \ | tahoe \ + | thumbv7* \ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ | tron \ | ubicom32 \ @@ -1275,8 +1284,47 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if [ x$os != x ] +if test x$basic_os != x then + +# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just +# set os. +case $basic_os in + gnu/linux*) + kernel=linux + os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|') + ;; + os2-emx) + kernel=os2 + os=$(echo $basic_os | sed -e 's|os2-emx|emx|') + ;; + nto-qnx*) + kernel=nto + os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|') + ;; + *-*) + # shellcheck disable=SC2162 + IFS="-" read kernel os <<EOF +$basic_os +EOF + ;; + # Default OS when just kernel was specified + nto*) + kernel=nto + os=$(echo $basic_os | sed -e 's|nto|qnx|') + ;; + linux*) + kernel=linux + os=$(echo $basic_os | sed -e 's|linux|gnu|') + ;; + *) + kernel= + os=$basic_os + ;; +esac + +# Now, normalize the OS (knowing we just have one component, it's not a kernel, +# etc.) case $os in # First match some system type aliases that might get confused # with valid system types. @@ -1288,7 +1336,7 @@ case $os in os=cnk ;; solaris1 | solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` + os=$(echo $os | sed -e 's|solaris1|sunos4|') ;; solaris) os=solaris2 @@ -1296,9 +1344,6 @@ case $os in unixware*) os=sysv4.2uw ;; - gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; # es1800 is here to avoid being matched by es* (a different OS) es1800*) os=ose @@ -1320,12 +1365,9 @@ case $os in os=sco3.2v4 ;; sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + os=$(echo $os | sed -e 's/sco3.2./sco3.2v/') ;; - sco3.2v[4-9]* | sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - ;; - scout) + sco*v* | scout) # Don't match below ;; sco*) @@ -1334,79 +1376,26 @@ case $os in psos*) os=psos ;; - # Now accept the basic system types. - # The portable systems comes first. - # Each alternative MUST end in a * to match a version number. - # sysv* is not here because it comes later, after sysvr4. - gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ - | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\ - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ - | sym* | kopensolaris* | plan9* \ - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ - | aos* | aros* | cloudabi* | sortix* \ - | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ - | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ - | knetbsd* | mirbsd* | netbsd* \ - | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \ - | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \ - | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ - | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ - | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \ - | chorusrdb* | cegcc* | glidix* \ - | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ - | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \ - | linux-newlib* | linux-musl* | linux-uclibc* \ - | uxpv* | beos* | mpeix* | udk* | moxiebox* \ - | interix* | uwin* | mks* | rhapsody* | darwin* \ - | openstep* | oskit* | conix* | pw32* | nonstopux* \ - | storm-chaos* | tops10* | tenex* | tops20* | its* \ - | os2* | vos* | palmos* | uclinux* | nucleus* \ - | morphos* | superux* | rtmk* | windiss* \ - | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ - | skyos* | haiku* | rdos* | toppers* | drops* | es* \ - | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ - | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ - | nsk* | powerunix) - # Remember, each alternative MUST END IN *, to match a version number. - ;; qnx*) - case $cpu in - x86 | i*86) - ;; - *) - os=nto-$os - ;; - esac + os=qnx ;; hiux*) os=hiuxwe2 ;; - nto-qnx*) - ;; - nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - sim | xray | os68k* | v88r* \ - | windows* | osx | abug | netware* | os9* \ - | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) - ;; - linux-dietlibc) - os=linux-dietlibc - ;; - linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; lynx*178) os=lynxos178 ;; lynx*5) os=lynxos5 ;; + lynxos*) + # don't get caught up in next wildcard + ;; lynx*) os=lynxos ;; - mac*) - os=`echo "$os" | sed -e 's|mac|macos|'` + mac[0-9]*) + os=$(echo "$os" | sed -e 's|mac|macos|') ;; opened*) os=openedition @@ -1415,10 +1404,10 @@ case $os in os=os400 ;; sunos5*) - os=`echo "$os" | sed -e 's|sunos5|solaris2|'` + os=$(echo "$os" | sed -e 's|sunos5|solaris2|') ;; sunos6*) - os=`echo "$os" | sed -e 's|sunos6|solaris3|'` + os=$(echo "$os" | sed -e 's|sunos6|solaris3|') ;; wince*) os=wince @@ -1452,7 +1441,7 @@ case $os in ;; # Preserve the version number of sinix5. sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` + os=$(echo $os | sed -e 's|sinix|sysv|') ;; sinix*) os=sysv4 @@ -1475,18 +1464,12 @@ case $os in sysvr4) os=sysv4 ;; - # This must come after sysvr4. - sysv*) - ;; ose*) os=ose ;; *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) os=mint ;; - zvmoe) - os=zvmoe - ;; dicos*) os=dicos ;; @@ -1503,19 +1486,11 @@ case $os in ;; esac ;; - nacl*) - ;; - ios) - ;; - none) - ;; - *-eabi) - ;; *) - echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 - exit 1 + # No normalization, but not necessarily accepted, that comes below. ;; esac + else # Here we handle the default operating systems that come with various machines. @@ -1528,6 +1503,7 @@ else # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. +kernel= case $cpu-$vendor in score-*) os=elf @@ -1539,7 +1515,8 @@ case $cpu-$vendor in os=riscix1.2 ;; arm*-rebel) - os=linux + kernel=linux + os=gnu ;; arm*-semi) os=aout @@ -1705,84 +1682,178 @@ case $cpu-$vendor in os=none ;; esac + fi +# Now, validate our (potentially fixed-up) OS. +case $os in + # Sometimes we do "kernel-libc", so those need to count as OSes. + musl* | newlib* | uclibc*) + ;; + # Likewise for "kernel-abi" + eabi* | gnueabi*) + ;; + # VxWorks passes extra cpu info in the 4th filed. + simlinux | simwindows | spe) + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ + | os9* | macos* | osx* | ios* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | mirbsd* | netbsd* | dicos* | openedition* | ose* \ + | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* | serenity* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | mint* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ + | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + none) + ;; + *) + echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) + ;; + uclinux-uclibc* ) + ;; + -dietlibc* | -newlib* | -musl* | -uclibc* ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + kfreebsd*-gnu* | kopensolaris*-gnu*) + ;; + vxworks-simlinux | vxworks-simwindows | vxworks-spe) + ;; + nto-qnx*) + ;; + os2-emx) + ;; + *-eabi* | *-gnueabi*) + ;; + -*) + # Blank kernel with real OS is always fine. + ;; + *-*) + echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 + exit 1 + ;; +esac + # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) - case $os in - riscix*) + case $cpu-$os in + *-riscix*) vendor=acorn ;; - sunos*) + *-sunos*) vendor=sun ;; - cnk*|-aix*) + *-cnk* | *-aix*) vendor=ibm ;; - beos*) + *-beos*) vendor=be ;; - hpux*) + *-hpux*) vendor=hp ;; - mpeix*) + *-mpeix*) vendor=hp ;; - hiux*) + *-hiux*) vendor=hitachi ;; - unos*) + *-unos*) vendor=crds ;; - dgux*) + *-dgux*) vendor=dg ;; - luna*) + *-luna*) vendor=omron ;; - genix*) + *-genix*) vendor=ns ;; - clix*) + *-clix*) vendor=intergraph ;; - mvs* | opened*) + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) vendor=ibm ;; - os400*) + s390-* | s390x-*) vendor=ibm ;; - ptx*) + *-ptx*) vendor=sequent ;; - tpf*) + *-tpf*) vendor=ibm ;; - vxsim* | vxworks* | windiss*) + *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; - aux*) + *-aux*) vendor=apple ;; - hms*) + *-hms*) vendor=hitachi ;; - mpw* | macos*) + *-mpw* | *-macos*) vendor=apple ;; - *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; - vos*) + *-vos*) vendor=stratus ;; esac ;; esac -echo "$cpu-$vendor-$os" +echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 6b3b293140..f879d176f5 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -26,7 +26,7 @@ $(package)_config_libraries=filesystem,system,test $(package)_cxxflags=-std=c++17 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC $(package)_cxxflags_android=-fPIC -$(package)_cxxflags_darwin=-fcf-protection=full +$(package)_cxxflags_x86_64_darwin=-fcf-protection=full endef define $(package)_preprocess_cmds diff --git a/doc/fuzzing.md b/doc/fuzzing.md index e086840fe6..6fc9077e4c 100644 --- a/doc/fuzzing.md +++ b/doc/fuzzing.md @@ -16,7 +16,7 @@ $ FUZZ=process_message src/test/fuzz/fuzz # abort fuzzing using ctrl-c ``` -## Fuzzing harnesses, fuzzing output and fuzzing corpora +## Fuzzing harnesses and output [`process_message`](https://github.com/bitcoin/bitcoin/blob/master/src/test/fuzz/process_message.cpp) is a fuzzing harness for the [`ProcessMessage(...)` function (`net_processing`)](https://github.com/bitcoin/bitcoin/blob/master/src/net_processing.cpp). The available fuzzing harnesses are found in [`src/test/fuzz/`](https://github.com/bitcoin/bitcoin/tree/master/src/test/fuzz). @@ -64,6 +64,8 @@ block^@M-^?M-^?M-^?M-^?M-^?nM-^?M-^? In this case the fuzzer managed to create a `block` message which when passed to `ProcessMessage(...)` increased coverage. +## Fuzzing corpora + The project's collection of seed corpora is found in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. To fuzz `process_message` using the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) seed corpus: @@ -81,6 +83,20 @@ INFO: seed corpus: files: 991 min: 1b max: 1858b total: 288291b rss: 150Mb … ``` +## Reproduce a fuzzer crash reported by the CI + +- `cd` into the `qa-assets` directory and update it with `git pull qa-assets` +- locate the crash case described in the CI output, e.g. `Test unit written to + ./crash-1bc91feec9fc00b107d97dc225a9f2cdaa078eb6` +- make sure to compile with all sanitizers, if they are needed (fuzzing runs + more slowly with sanitizers enabled, but a crash should be reproducible very + quickly from a crash case) +- run the fuzzer with the case number appended to the seed corpus path: + `FUZZ=process_message src/test/fuzz/fuzz + qa-assets/fuzz_seed_corpus/process_message/1bc91feec9fc00b107d97dc225a9f2cdaa078eb6` + +## Submit improved coverage + If you find coverage increasing inputs when fuzzing you are highly encouraged to submit them for inclusion in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. Every single pull request submitted against the Bitcoin Core repo is automatically tested against all inputs in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. Contributing new coverage increasing inputs is an easy way to help make Bitcoin Core more robust. diff --git a/doc/release-notes-20833.md b/doc/release-notes-20833.md new file mode 100644 index 0000000000..9a02bbd275 --- /dev/null +++ b/doc/release-notes-20833.md @@ -0,0 +1,12 @@ +Updated RPCs +------------ + +- The `testmempoolaccept` RPC now accepts multiple transactions (still experimental at the moment, + API may be unstable). This is intended for testing transaction packages with dependency + relationships; it is not recommended for batch-validating independent transactions. In addition to + mempool policy, package policies apply: the list cannot contain more than 25 transactions or have a + total size exceeding 101K virtual bytes, and cannot conflict with (spend the same inputs as) each other or + the mempool, even if it would be a valid BIP125 replace-by-fee. There are some known limitations to + the accuracy of the test accept: it's possible for `testmempoolaccept` to return "allowed"=True for a + group of transactions, but "too-long-mempool-chain" if they are actually submitted. (#20833) + diff --git a/src/Makefile.am b/src/Makefile.am index 770bb76226..66cb7cec2a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -193,6 +193,7 @@ BITCOIN_CORE_H = \ outputtype.h \ policy/feerate.h \ policy/fees.h \ + policy/packages.h \ policy/policy.h \ policy/rbf.h \ policy/settings.h \ @@ -253,6 +254,7 @@ BITCOIN_CORE_H = \ util/moneystr.h \ util/rbf.h \ util/readwritefile.h \ + util/serfloat.h \ util/settings.h \ util/sock.h \ util/spanparsing.h \ @@ -283,10 +285,13 @@ BITCOIN_CORE_H = \ wallet/fees.h \ wallet/ismine.h \ wallet/load.h \ + wallet/receive.h \ wallet/rpcwallet.h \ wallet/salvage.h \ wallet/scriptpubkeyman.h \ + wallet/spend.h \ wallet/sqlite.h \ + wallet/transaction.h \ wallet/wallet.h \ wallet/walletdb.h \ wallet/wallettool.h \ @@ -404,9 +409,12 @@ libbitcoin_wallet_a_SOURCES = \ wallet/fees.cpp \ wallet/interfaces.cpp \ wallet/load.cpp \ + wallet/receive.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/scriptpubkeyman.cpp \ + wallet/spend.cpp \ + wallet/transaction.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ wallet/walletutil.cpp \ @@ -594,6 +602,7 @@ libbitcoin_util_a_SOURCES = \ util/settings.cpp \ util/thread.cpp \ util/threadnames.cpp \ + util/serfloat.cpp \ util/spanparsing.cpp \ util/strencodings.cpp \ util/string.cpp \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 30edb1e82d..9ad66bc85b 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -288,7 +288,7 @@ RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png) BITCOIN_RC = qt/res/bitcoin-qt-res.rc -BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS +BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS -DQT_USE_QSTRINGBUILDER qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(QR_CFLAGS) @@ -382,7 +382,8 @@ bitcoin_qt_clean: FORCE bitcoin_qt : qt/bitcoin-qt$(EXEEXT) APK_LIB_DIR = qt/android/libs/$(ANDROID_ARCH) -QT_BASE_PATH = $(shell find ../depends/sources/ -maxdepth 1 -type f -regex ".*qtbase.*\.tar.xz") +QT_BASE_VERSION = $(lastword $(shell $(MOC) --version)) +QT_BASE_PATH = $(shell find ../depends/sources/ -maxdepth 1 -type f -regex ".*qtbase.*$(QT_BASE_VERSION)\.tar.xz") QT_BASE_TLD = $(shell tar tf $(QT_BASE_PATH) --exclude='*/*') bitcoin_qt_apk: FORCE diff --git a/src/Makefile.test.include b/src/Makefile.test.include index dc79ea3125..105d09f730 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -121,6 +121,7 @@ BITCOIN_TESTS =\ test/script_tests.cpp \ test/script_standard_tests.cpp \ test/scriptnum_tests.cpp \ + test/serfloat_tests.cpp \ test/serialize_tests.cpp \ test/settings_tests.cpp \ test/sighash_tests.cpp \ @@ -257,7 +258,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/netaddress.cpp \ test/fuzz/netbase_dns_lookup.cpp \ test/fuzz/node_eviction.cpp \ - test/fuzz/p2p_transport_deserializer.cpp \ + test/fuzz/p2p_transport_serialization.cpp \ test/fuzz/parse_hd_keypath.cpp \ test/fuzz/parse_iso8601.cpp \ test/fuzz/parse_numbers.cpp \ diff --git a/src/addrman.h b/src/addrman.h index eaedfd318c..41994288db 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -172,139 +172,6 @@ static const int64_t ADDRMAN_TEST_WINDOW = 40*60; // 40 minutes */ class CAddrMan { -friend class CAddrManTest; -protected: - //! critical section to protect the inner data structures - mutable RecursiveMutex cs; - -private: - //! Serialization versions. - enum Format : uint8_t { - V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88 - V1_DETERMINISTIC = 1, //!< for pre-asmap files - V2_ASMAP = 2, //!< for files including asmap version - V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format - }; - - //! The maximum format this software knows it can unserialize. Also, we always serialize - //! in this format. - //! The format (first byte in the serialized stream) can be higher than this and - //! still this software may be able to unserialize the file - if the second byte - //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this. - static constexpr Format FILE_FORMAT = Format::V3_BIP155; - - //! The initial value of a field that is incremented every time an incompatible format - //! change is made (such that old software versions would not be able to parse and - //! understand the new file format). This is 32 because we overtook the "key size" - //! field which was 32 historically. - //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead. - static constexpr uint8_t INCOMPATIBILITY_BASE = 32; - - //! last used nId - int nIdCount GUARDED_BY(cs); - - //! table with information about all nIds - std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs); - - //! find an nId based on its network address - std::map<CNetAddr, int> mapAddr GUARDED_BY(cs); - - //! randomly-ordered vector of all nIds - std::vector<int> vRandom GUARDED_BY(cs); - - // number of "tried" entries - int nTried GUARDED_BY(cs); - - //! list of "tried" buckets - int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs); - - //! number of (unique) "new" entries - int nNew GUARDED_BY(cs); - - //! list of "new" buckets - int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs); - - //! last time Good was called (memory only) - int64_t nLastGood GUARDED_BY(cs); - - //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions. - std::set<int> m_tried_collisions; - -protected: - //! secret key to randomize bucket select with - uint256 nKey; - - //! Source of random numbers for randomization in inner loops - FastRandomContext insecure_rand; - - //! Find an entry. - CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! find an entry, creating it if necessary. - //! nTime and nServices of the found node are updated, if necessary. - CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Swap two elements in vRandom. - void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Move an entry from the "new" table(s) to the "tried" table - void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Delete an entry. It must not be in tried, and have refcount 0. - void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Clear a position in a "new" table. This is the only place where entries are actually deleted. - void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Mark an entry "good", possibly moving it from "new" to "tried". - void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Add an entry to the "new" table. - bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Mark an entry as attempted to connect. - void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. - CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. - void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Return a random to-be-evicted tried table address. - CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs); - -#ifdef DEBUG_ADDRMAN - //! Perform consistency check. Returns an error code or zero. - int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs); -#endif - - /** - * Return all or many randomly selected addresses, optionally by network. - * - * @param[out] vAddr Vector of randomly selected addresses from vRandom. - * @param[in] max_addresses Maximum number of addresses to return (0 = all). - * @param[in] max_pct Maximum percentage of addresses to return (0 = all). - * @param[in] network Select only addresses of this network (nullopt = all). - */ - void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) EXCLUSIVE_LOCKS_REQUIRED(cs); - - /** We have successfully connected to this peer. Calling this function - * updates the CAddress's nTime, which is used in our IsTerrible() - * decisions and gossiped to peers. Callers should be careful that updating - * this information doesn't leak topology information to network spies. - * - * net_processing calls this function when it *disconnects* from a peer to - * not leak information about currently connected peers. - * - * @param[in] addr The address of the peer we were connected to - * @param[in] nTime The time that we were last connected to this peer - */ - void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); - - //! Update an entry's service bits. - void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs); - public: // Compressed IP->ASN mapping, loaded from a file when a node starts. // Should be always empty if no file was provided. @@ -325,7 +192,6 @@ public: // Read asmap from provided binary file static std::vector<bool> DecodeAsmap(fs::path path); - /** * Serialized format. * * format version byte (@see `Format`) @@ -759,6 +625,138 @@ public: Check(); } +protected: + //! secret key to randomize bucket select with + uint256 nKey; + + //! Source of random numbers for randomization in inner loops + FastRandomContext insecure_rand; + +private: + //! critical section to protect the inner data structures + mutable RecursiveMutex cs; + + //! Serialization versions. + enum Format : uint8_t { + V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88 + V1_DETERMINISTIC = 1, //!< for pre-asmap files + V2_ASMAP = 2, //!< for files including asmap version + V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format + }; + + //! The maximum format this software knows it can unserialize. Also, we always serialize + //! in this format. + //! The format (first byte in the serialized stream) can be higher than this and + //! still this software may be able to unserialize the file - if the second byte + //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this. + static constexpr Format FILE_FORMAT = Format::V3_BIP155; + + //! The initial value of a field that is incremented every time an incompatible format + //! change is made (such that old software versions would not be able to parse and + //! understand the new file format). This is 32 because we overtook the "key size" + //! field which was 32 historically. + //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead. + static constexpr uint8_t INCOMPATIBILITY_BASE = 32; + + //! last used nId + int nIdCount GUARDED_BY(cs); + + //! table with information about all nIds + std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs); + + //! find an nId based on its network address + std::map<CNetAddr, int> mapAddr GUARDED_BY(cs); + + //! randomly-ordered vector of all nIds + std::vector<int> vRandom GUARDED_BY(cs); + + // number of "tried" entries + int nTried GUARDED_BY(cs); + + //! list of "tried" buckets + int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs); + + //! number of (unique) "new" entries + int nNew GUARDED_BY(cs); + + //! list of "new" buckets + int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs); + + //! last time Good was called (memory only) + int64_t nLastGood GUARDED_BY(cs); + + //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions. + std::set<int> m_tried_collisions; + + //! Find an entry. + CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! find an entry, creating it if necessary. + //! nTime and nServices of the found node are updated, if necessary. + CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Swap two elements in vRandom. + void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Move an entry from the "new" table(s) to the "tried" table + void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Delete an entry. It must not be in tried, and have refcount 0. + void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Clear a position in a "new" table. This is the only place where entries are actually deleted. + void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Mark an entry "good", possibly moving it from "new" to "tried". + void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Add an entry to the "new" table. + bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Mark an entry as attempted to connect. + void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. + CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. + void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Return a random to-be-evicted tried table address. + CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs); + +#ifdef DEBUG_ADDRMAN + //! Perform consistency check. Returns an error code or zero. + int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs); +#endif + + /** + * Return all or many randomly selected addresses, optionally by network. + * + * @param[out] vAddr Vector of randomly selected addresses from vRandom. + * @param[in] max_addresses Maximum number of addresses to return (0 = all). + * @param[in] max_pct Maximum percentage of addresses to return (0 = all). + * @param[in] network Select only addresses of this network (nullopt = all). + */ + void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) EXCLUSIVE_LOCKS_REQUIRED(cs); + + /** We have successfully connected to this peer. Calling this function + * updates the CAddress's nTime, which is used in our IsTerrible() + * decisions and gossiped to peers. Callers should be careful that updating + * this information doesn't leak topology information to network spies. + * + * net_processing calls this function when it *disconnects* from a peer to + * not leak information about currently connected peers. + * + * @param[in] addr The address of the peer we were connected to + * @param[in] nTime The time that we were last connected to this peer + */ + void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); + + //! Update an entry's service bits. + void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs); + + friend class CAddrManTest; }; #endif // BITCOIN_ADDRMAN_H diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 80acdec044..c279a9af2f 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -49,15 +49,14 @@ static void CoinSelection(benchmark::Bench& bench) } const CoinEligibilityFilter filter_standard(1, 6, 0); - const CoinSelectionParams coin_selection_params(/* use_bnb= */ true, /* change_output_size= */ 34, + const CoinSelectionParams coin_selection_params(/* change_output_size= */ 34, /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0), /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); bench.run([&] { std::set<CInputCoin> setCoinsRet; CAmount nValueRet; - bool bnb_used; - bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params, bnb_used); + bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params); assert(success); assert(nValueRet == 1003 * COIN); assert(setCoinsRet.size() == 2); @@ -101,12 +100,11 @@ static void BnBExhaustion(benchmark::Bench& bench) std::vector<OutputGroup> utxo_pool; CoinSet selection; CAmount value_ret = 0; - CAmount not_input_fees = 0; bench.run([&] { // Benchmark CAmount target = make_hard_case(17, utxo_pool); - SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret, not_input_fees); // Should exhaust + SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret); // Should exhaust // Cleanup utxo_pool.clear(); diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 2e31daea83..994d420885 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -1206,12 +1206,17 @@ static const uint8_t chainparams_seed_main[] = { }; static const uint8_t chainparams_seed_test[] = { - 0x03,0x0a,0x99,0xcb,0x26,0x31,0xba,0x48,0x51,0x31,0x39,0x0d,0x47,0x9d, - 0x03,0x0a,0x44,0xf4,0xf4,0xf0,0xbf,0xf7,0x7e,0x6d,0xc4,0xe8,0x47,0x9d, - 0x03,0x0a,0x6a,0x8b,0xd2,0x78,0x3f,0x7a,0xf8,0x92,0x8f,0x80,0x47,0x9d, - 0x03,0x0a,0xe6,0x4e,0xa4,0x47,0x4e,0x2a,0xfe,0xe8,0x95,0xcc,0x47,0x9d, - 0x03,0x0a,0x9f,0xae,0x9f,0x59,0x0b,0x3f,0x31,0x3a,0x8a,0x5f,0x47,0x9d, - 0x03,0x0a,0x47,0xb1,0xe4,0x55,0xd1,0xb0,0x14,0x3f,0xb6,0xdb,0x47,0x9d, - 0x03,0x0a,0xa0,0x60,0x9e,0x46,0x54,0xdb,0x61,0x3b,0xb2,0x6f,0x47,0x9d, + 0x04,0x20,0xdf,0x55,0xaa,0x83,0xd5,0xc5,0xb8,0xe7,0x75,0x78,0xd4,0x29,0x51,0x4b,0x26,0x1c,0x23,0xdf,0x28,0x4d,0x29,0x85,0x07,0xb5,0xe2,0x29,0x69,0x3e,0x25,0xbb,0x61,0xcf,0x47,0x9d, + 0x04,0x20,0x0a,0xdd,0xa2,0x48,0xb5,0x56,0xa3,0x1f,0xca,0x3c,0x4c,0x9e,0xca,0x6e,0xb3,0xd5,0x5e,0x68,0xf6,0x28,0x31,0x57,0x24,0xfb,0x9d,0x2b,0x55,0x4f,0xd7,0x90,0x62,0xd3,0x47,0x9d, + 0x04,0x20,0x2d,0x04,0xa1,0x4a,0xd4,0x7c,0x7b,0x16,0x2e,0xb7,0xd2,0xa1,0x08,0xc5,0xd2,0xbd,0x53,0x87,0x34,0xdc,0x38,0x26,0xca,0x56,0xf2,0xac,0xc5,0x62,0x70,0x72,0x3f,0x63,0x47,0x9d, + 0x04,0x20,0x30,0x57,0x85,0xe0,0x02,0x4a,0xd1,0x31,0xeb,0x16,0x1b,0x1d,0xa8,0x43,0x0b,0xb4,0xc6,0xac,0x7d,0x46,0x24,0x0b,0x55,0x9d,0x16,0xe6,0x46,0x03,0x72,0xfe,0xd4,0xef,0x47,0x9d, + 0x04,0x20,0x36,0x6c,0xf1,0xd2,0xbb,0xda,0xff,0x8c,0x93,0x61,0x10,0xf2,0x9d,0xa1,0xa4,0x0a,0x30,0x9b,0x0c,0x69,0x6d,0xaa,0xd4,0x9c,0xfd,0xb5,0x5b,0x5e,0x30,0x9f,0xf3,0x13,0x47,0x9d, + 0x04,0x20,0x3e,0xe2,0xf3,0xe5,0xc5,0xbe,0x61,0xdd,0x4c,0x3e,0xdb,0x0d,0xd2,0xf9,0x42,0xe3,0x31,0xb2,0xa8,0x51,0x31,0xf6,0xce,0xc2,0x38,0x20,0x27,0x39,0x73,0x68,0x5a,0x42,0x47,0x9d, + 0x04,0x20,0x51,0x79,0x05,0x9c,0x8a,0xdf,0x03,0xb5,0x1b,0x17,0xc3,0x86,0xb6,0x54,0xcc,0xe0,0x6e,0x58,0xa6,0x41,0x4c,0xcc,0x0c,0x60,0x08,0xa6,0x0f,0x1d,0x11,0xd8,0x29,0xa6,0x47,0x9d, + 0x04,0x20,0x60,0xbe,0xae,0x7d,0xa3,0x4d,0x6a,0x71,0x1a,0x5d,0xe5,0x98,0x9c,0xde,0xa0,0x99,0x39,0x19,0xd3,0x01,0x0a,0x5d,0x1c,0x21,0x43,0x94,0x92,0x71,0x5d,0x77,0xd7,0xdf,0x47,0x9d, + 0x04,0x20,0x64,0x4e,0x86,0xa1,0x02,0xa1,0x8a,0xef,0xb0,0xd1,0xb5,0x77,0x69,0xb9,0x6a,0xdc,0xdf,0x35,0x8a,0xda,0xa4,0x3e,0x83,0xfa,0x50,0xe6,0xca,0x0e,0x2b,0x99,0x0a,0x17,0x47,0x9d, + 0x04,0x20,0xa2,0x28,0x3c,0x5a,0x5b,0x82,0x32,0x66,0x11,0xe5,0x71,0xff,0x6b,0x25,0x92,0x75,0xdd,0x7a,0x4f,0x90,0x8b,0x1d,0x34,0xa4,0xf1,0x6e,0xb9,0xfb,0xb5,0x2e,0x7c,0x7f,0x47,0x9d, + 0x04,0x20,0xc8,0xb5,0x6a,0xba,0x02,0x26,0x45,0x12,0xfb,0x93,0x8a,0x51,0xe4,0xb0,0xf3,0x94,0xb7,0xc0,0x74,0x72,0xeb,0x67,0x91,0x9e,0x04,0x36,0x6a,0x4b,0xef,0x0d,0x88,0xfe,0x47,0x9d, + 0x04,0x20,0xc8,0xfa,0xcd,0x8c,0xc3,0x6f,0x3c,0xd0,0x27,0x7e,0x7d,0xeb,0x51,0x01,0x65,0xb6,0x9e,0x02,0x09,0x64,0xf4,0x87,0x78,0x7b,0x8f,0x9d,0xaf,0x3b,0xa5,0xcc,0x56,0x2c,0x47,0x9d, }; #endif // BITCOIN_CHAINPARAMSSEEDS_H diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h index 5f50cde3ff..7a254c3b67 100644 --- a/src/compat/assumptions.h +++ b/src/compat/assumptions.h @@ -36,11 +36,6 @@ static_assert(std::numeric_limits<double>::is_iec559, "IEEE 754 double assumed") // Example(s): Everywhere :-) static_assert(std::numeric_limits<unsigned char>::digits == 8, "8-bit byte assumed"); -// Assumption: We assume floating-point widths. -// Example(s): Type punning in serialization code (ser_{float,double}_to_uint{32,64}). -static_assert(sizeof(float) == 4, "32-bit float assumed"); -static_assert(sizeof(double) == 8, "64-bit double assumed"); - // Assumption: We assume integer widths. // Example(s): GetSizeOfCompactSize and WriteCompactSize in the serialization // code. diff --git a/src/init.cpp b/src/init.cpp index 593128747e..a835df0572 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -390,7 +390,6 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-loadblock=<file>", "Imports blocks from external file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index a3f4b29cd3..0b83f756b3 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -364,6 +364,9 @@ private: */ void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable); + /** Send `feefilter` message. */ + void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + const CChainParams& m_chainparams; CConnman& m_connman; CAddrMan& m_addrman; @@ -4276,6 +4279,49 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros } } +void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds current_time) +{ + AssertLockHeld(cs_main); + + if (m_ignore_incoming_txs) return; + if (!pto.m_tx_relay) return; + if (pto.GetCommonVersion() < FEEFILTER_VERSION) return; + // peers with the forcerelay permission should not filter txs to us + if (pto.HasPermission(NetPermissionFlags::ForceRelay)) return; + + CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); + static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}}; + + if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) { + // Received tx-inv messages are discarded when the active + // chainstate is in IBD, so tell the peer to not send them. + currentFilter = MAX_MONEY; + } else { + static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)}; + if (pto.m_tx_relay->lastSentFeeFilter == MAX_FILTER) { + // Send the current filter if we sent MAX_FILTER previously + // and made it out of IBD. + pto.m_tx_relay->m_next_send_feefilter = 0us; + } + } + if (current_time > pto.m_tx_relay->m_next_send_feefilter) { + CAmount filterToSend = g_filter_rounder.round(currentFilter); + // We always have a fee filter of at least minRelayTxFee + filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK()); + if (filterToSend != pto.m_tx_relay->lastSentFeeFilter) { + m_connman.PushMessage(&pto, CNetMsgMaker(pto.GetCommonVersion()).Make(NetMsgType::FEEFILTER, filterToSend)); + pto.m_tx_relay->lastSentFeeFilter = filterToSend; + } + pto.m_tx_relay->m_next_send_feefilter = PoissonNextSend(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL); + } + // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY + // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY. + else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < pto.m_tx_relay->m_next_send_feefilter && + (currentFilter < 3 * pto.m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto.m_tx_relay->lastSentFeeFilter / 3)) { + pto.m_tx_relay->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY); + } +} + namespace { class CompareInvMempoolOrder { @@ -4762,46 +4808,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) if (!vGetData.empty()) m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); - // - // Message: feefilter - // - if (pto->m_tx_relay != nullptr && - !m_ignore_incoming_txs && - pto->GetCommonVersion() >= FEEFILTER_VERSION && - gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && - !pto->HasPermission(NetPermissionFlags::ForceRelay) // peers with the forcerelay permission should not filter txs to us - ) { - CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); - static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}}; - if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) { - // Received tx-inv messages are discarded when the active - // chainstate is in IBD, so tell the peer to not send them. - currentFilter = MAX_MONEY; - } else { - static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)}; - if (pto->m_tx_relay->lastSentFeeFilter == MAX_FILTER) { - // Send the current filter if we sent MAX_FILTER previously - // and made it out of IBD. - pto->m_tx_relay->m_next_send_feefilter = 0us; - } - } - if (current_time > pto->m_tx_relay->m_next_send_feefilter) { - CAmount filterToSend = g_filter_rounder.round(currentFilter); - // We always have a fee filter of at least minRelayTxFee - filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK()); - if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) { - m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); - pto->m_tx_relay->lastSentFeeFilter = filterToSend; - } - pto->m_tx_relay->m_next_send_feefilter = PoissonNextSend(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL); - } - // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY - // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY. - else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < pto->m_tx_relay->m_next_send_feefilter && - (currentFilter < 3 * pto->m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto->m_tx_relay->lastSentFeeFilter / 3)) { - pto->m_tx_relay->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY); - } - } + MaybeSendFeefilter(*pto, current_time); } // release cs_main return true; } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 52c3362166..2ae5798ebe 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -10,6 +10,7 @@ #include <logging.h> #include <streams.h> #include <txmempool.h> +#include <util/serfloat.h> #include <util/system.h> static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat"; @@ -26,6 +27,25 @@ std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) assert(false); } +namespace { + +struct EncodedDoubleFormatter +{ + template<typename Stream> void Ser(Stream &s, double v) + { + s << EncodeDouble(v); + } + + template<typename Stream> void Unser(Stream& s, double& v) + { + uint64_t encoded; + s >> encoded; + v = DecodeDouble(encoded); + } +}; + +} // namespace + /** * We will instantiate an instance of this class to track transactions that were * included in a block. We will lump transactions into a bucket according to their @@ -356,12 +376,12 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, void TxConfirmStats::Write(CAutoFile& fileout) const { - fileout << decay; + fileout << Using<EncodedDoubleFormatter>(decay); fileout << scale; - fileout << m_feerate_avg; - fileout << txCtAvg; - fileout << confAvg; - fileout << failAvg; + fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg); + fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg); + fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg); + fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg); } void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets) @@ -372,7 +392,7 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets size_t maxConfirms, maxPeriods; // The current version will store the decay with each individual TxConfirmStats and also keep a scale factor - filein >> decay; + filein >> Using<EncodedDoubleFormatter>(decay); if (decay <= 0 || decay >= 1) { throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)"); } @@ -381,15 +401,15 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets throw std::runtime_error("Corrupt estimates file. Scale must be non-zero"); } - filein >> m_feerate_avg; + filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg); if (m_feerate_avg.size() != numBuckets) { throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count"); } - filein >> txCtAvg; + filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg); if (txCtAvg.size() != numBuckets) { throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count"); } - filein >> confAvg; + filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg); maxPeriods = confAvg.size(); maxConfirms = scale * maxPeriods; @@ -402,7 +422,7 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets } } - filein >> failAvg; + filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg); if (maxPeriods != failAvg.size()) { throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures"); } @@ -884,7 +904,7 @@ bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const else { fileout << historicalFirst << historicalBest; } - fileout << buckets; + fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets); feeStats->Write(fileout); shortStats->Write(fileout); longStats->Write(fileout); @@ -920,7 +940,7 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein) throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid"); } std::vector<double> fileBuckets; - filein >> fileBuckets; + filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets); size_t numBuckets = fileBuckets.size(); if (numBuckets <= 1 || numBuckets > 1000) { throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets"); diff --git a/src/policy/packages.h b/src/policy/packages.h new file mode 100644 index 0000000000..4b1463dcb3 --- /dev/null +++ b/src/policy/packages.h @@ -0,0 +1,34 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_POLICY_PACKAGES_H +#define BITCOIN_POLICY_PACKAGES_H + +#include <consensus/validation.h> +#include <primitives/transaction.h> + +#include <vector> + +/** Default maximum number of transactions in a package. */ +static constexpr uint32_t MAX_PACKAGE_COUNT{25}; +/** Default maximum total virtual size of transactions in a package in KvB. */ +static constexpr uint32_t MAX_PACKAGE_SIZE{101}; + +/** A "reason" why a package was invalid. It may be that one or more of the included + * transactions is invalid or the package itself violates our rules. + * We don't distinguish between consensus and policy violations right now. + */ +enum class PackageValidationResult { + PCKG_RESULT_UNSET = 0, //!< Initial value. The package has not yet been rejected. + PCKG_POLICY, //!< The package itself is invalid (e.g. too many transactions). + PCKG_TX, //!< At least one tx is invalid. +}; + +/** A package is an ordered list of transactions. The transactions cannot conflict with (spend the + * same inputs as) one another. */ +using Package = std::vector<CTransactionRef>; + +class PackageValidationState : public ValidationState<PackageValidationResult> {}; + +#endif // BITCOIN_POLICY_PACKAGES_H diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index a816a0764c..7024fc7654 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -281,7 +281,9 @@ void AddressBookPage::on_exportButton_clicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Address List"), QString(), - tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr); + /*: Expanded name of the CSV file format. + See https://en.wikipedia.org/wiki/Comma-separated_values */ + tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr); if (filename.isNull()) return; @@ -295,8 +297,9 @@ void AddressBookPage::on_exportButton_clicked() if(!writer.write()) { QMessageBox::critical(this, tr("Exporting Failed"), - //: %1 is a name of the file (e.g., "addrbook.csv") that the bitcoin addresses were exported to. - tr("There was an error trying to save the address list to %1. Please try again.", "An error message.").arg(filename)); + /*: An error message. %1 is a stand-in argument for the name + of the file we attempted to save to. */ + tr("There was an error trying to save the address list to %1. Please try again.").arg(filename)); } } diff --git a/src/qt/android/.gitignore b/src/qt/android/.gitignore new file mode 100644 index 0000000000..74cf42f934 --- /dev/null +++ b/src/qt/android/.gitignore @@ -0,0 +1,9 @@ +/.gradle +/build +/gradle/wrapper +/gradlew* +/libs +/res/layout +/res/values* +/src/org/kde +/src/org/qtproject diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index dffdd5158b..69948402d0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -50,7 +50,6 @@ #include <QLocale> #include <QMessageBox> #include <QSettings> -#include <QStringBuilder> #include <QThread> #include <QTimer> #include <QTranslator> @@ -419,8 +418,8 @@ void BitcoinApplication::handleRunawayException(const QString &message) { QMessageBox::critical( nullptr, tr("Runaway exception"), - tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) % - QLatin1String("<br><br>") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT)); + tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + + QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT)); ::exit(EXIT_FAILURE); } @@ -430,8 +429,8 @@ void BitcoinApplication::handleNonFatalException(const QString& message) QMessageBox::warning( nullptr, tr("Internal error"), tr("An internal error occurred. %1 will attempt to continue safely. This is " - "an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) % - QLatin1String("<br><br>") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT)); + "an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) + + QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT)); } WId BitcoinApplication::getMainWinId() const diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 17fffe2087..100022bac1 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -150,11 +150,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); - labelWalletEncryptionIcon = new QLabel(); - labelWalletHDStatusIcon = new QLabel(); - labelProxyIcon = new GUIUtil::ClickableLabel(); - connectionsControl = new GUIUtil::ClickableLabel(); - labelBlocksIcon = new GUIUtil::ClickableLabel(); + labelWalletEncryptionIcon = new GUIUtil::ThemedLabel(platformStyle); + labelWalletHDStatusIcon = new GUIUtil::ThemedLabel(platformStyle); + labelProxyIcon = new GUIUtil::ClickableLabel(platformStyle); + connectionsControl = new GUIUtil::ClickableLabel(platformStyle); + labelBlocksIcon = new GUIUtil::ClickableLabel(platformStyle); if(enableWallet) { frameBlocksLayout->addStretch(); @@ -925,7 +925,7 @@ void BitcoinGUI::updateNetworkState() tooltip = QString("<nobr>") + tooltip + QString("</nobr>"); connectionsControl->setToolTip(tooltip); - connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + connectionsControl->setThemedPixmap(icon, STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE); } void BitcoinGUI::setNumConnections(int count) @@ -1021,7 +1021,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer // Set icon state: spinning if catching up, tick otherwise if (secs < MAX_BLOCK_TIME_GAP) { tooltip = tr("Up to date") + QString(".<br>") + tooltip; - labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setThemedPixmap(QStringLiteral(":/icons/synced"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE); #ifdef ENABLE_WALLET if(walletFrame) @@ -1047,9 +1047,9 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer tooltip = tr("Catching up…") + QString("<br>") + tooltip; if(count != prevBlocks) { - labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString( - ":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0'))) - .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelBlocksIcon->setThemedPixmap( + QString(":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')), + STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE); spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES; } prevBlocks = count; @@ -1138,7 +1138,17 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty void BitcoinGUI::changeEvent(QEvent *e) { +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + overviewAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/overview"))); + sendCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/send"))); + receiveCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/receiving_addresses"))); + historyAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/history"))); + } +#endif + QMainWindow::changeEvent(e); + #ifndef Q_OS_MAC // Ignored on Mac if(e->type() == QEvent::WindowStateChange) { @@ -1256,7 +1266,7 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled) { - labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(privkeyDisabled ? ":/icons/eye" : hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletHDStatusIcon->setThemedPixmap(privkeyDisabled ? QStringLiteral(":/icons/eye") : hdEnabled ? QStringLiteral(":/icons/hd_enabled") : QStringLiteral(":/icons/hd_disabled"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE); labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>")); labelWalletHDStatusIcon->show(); // eventually disable the QLabel to set its opacity to 50% @@ -1275,7 +1285,7 @@ void BitcoinGUI::setEncryptionStatus(int status) break; case WalletModel::Unlocked: labelWalletEncryptionIcon->show(); - labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_open"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE); labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); @@ -1283,7 +1293,7 @@ void BitcoinGUI::setEncryptionStatus(int status) break; case WalletModel::Locked: labelWalletEncryptionIcon->show(); - labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_closed"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE); labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); @@ -1315,7 +1325,7 @@ void BitcoinGUI::updateProxyIcon() if (proxy_enabled) { if (!GUIUtil::HasPixmap(labelProxyIcon)) { QString ip_port_q = QString::fromStdString(ip_port); - labelProxyIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/proxy").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelProxyIcon->setThemedPixmap((":/icons/proxy"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE); labelProxyIcon->setToolTip(tr("Proxy is <b>enabled</b>: %1").arg(ip_port_q)); } else { labelProxyIcon->show(); @@ -1375,7 +1385,6 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress) progressDialog = new QProgressDialog(title, QString(), 0, 100); GUIUtil::PolishProgressDialog(progressDialog); progressDialog->setWindowModality(Qt::ApplicationModal); - progressDialog->setMinimumDuration(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); } else if (nProgress == 100) { @@ -1440,9 +1449,10 @@ bool BitcoinGUI::isPrivacyModeActivated() const return m_mask_values_action->isChecked(); } -UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) : - optionsModel(nullptr), - menu(nullptr) +UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) + : optionsModel(nullptr), + menu(nullptr), + m_platform_style{platformStyle} { createContextMenu(); setToolTip(tr("Unit to show amounts in. Click to select another unit.")); @@ -1455,7 +1465,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl } setMinimumSize(max_width, 0); setAlignment(Qt::AlignRight | Qt::AlignVCenter); - setStyleSheet(QString("QLabel { color : %1 }").arg(platformStyle->SingleColor().name())); + setStyleSheet(QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name())); } /** So that it responds to button clicks */ @@ -1464,6 +1474,18 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) onDisplayUnitsClicked(event->pos()); } +void UnitDisplayStatusBarControl::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + QString style = QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name()); + if (style != styleSheet()) { + setStyleSheet(style); + } + } +#endif +} + /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */ void UnitDisplayStatusBarControl::createContextMenu() { diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 147f19e68d..65c5331d32 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -9,6 +9,7 @@ #include <config/bitcoin-config.h> #endif +#include <qt/guiutil.h> #include <qt/optionsdialog.h> #include <amount.h> @@ -121,8 +122,8 @@ private: WalletFrame* walletFrame = nullptr; UnitDisplayStatusBarControl* unitDisplayControl = nullptr; - QLabel* labelWalletEncryptionIcon = nullptr; - QLabel* labelWalletHDStatusIcon = nullptr; + GUIUtil::ThemedLabel* labelWalletEncryptionIcon = nullptr; + GUIUtil::ThemedLabel* labelWalletHDStatusIcon = nullptr; GUIUtil::ClickableLabel* labelProxyIcon = nullptr; GUIUtil::ClickableLabel* connectionsControl = nullptr; GUIUtil::ClickableLabel* labelBlocksIcon = nullptr; @@ -333,10 +334,12 @@ public: protected: /** So that it responds to left-button clicks */ void mousePressEvent(QMouseEvent *event) override; + void changeEvent(QEvent* e) override; private: OptionsModel *optionsModel; QMenu* menu; + const PlatformStyle* m_platform_style; /** Shows context menu with Display Unit options by the mouse coordinates */ void onDisplayUnitsClicked(const QPoint& point); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index dd4df44ed9..bff253e58a 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -75,12 +75,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Prune: last wallet synchronisation goes beyond pruned data. You need to -" "reindex (download the whole blockchain again in case of pruned node)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"SQLiteDatabase: Failed to prepare the statement to fetch sqlite wallet " -"schema version: %s"), -QT_TRANSLATE_NOOP("bitcoin-core", "" -"SQLiteDatabase: Failed to prepare the statement to fetch the application id: " -"%s"), -QT_TRANSLATE_NOOP("bitcoin-core", "" "SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is " "supported"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -201,8 +195,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, becau QT_TRANSLATE_NOOP("bitcoin-core", "Replaying blocks…"), QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning…"), QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to execute statement to verify database: %s"), -QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to fetch sqlite wallet schema version: %s"), -QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to fetch the application id: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to prepare statement to verify database: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to read database verification error: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Unexpected application id. Expected %u, got %u"), @@ -222,7 +214,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "This is the minimum transaction fee you pay o QT_TRANSLATE_NOOP("bitcoin-core", "This is the transaction fee you will pay if you send a transaction."), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must not be negative"), -QT_TRANSLATE_NOOP("bitcoin-core", "Transaction fee and change calculation failed"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction has too long of a mempool chain"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction must have at least one recipient"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"), diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index daea2f9cab..8ae0648141 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -562,6 +562,15 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * label->setVisible(nChange < 0); } +void CoinControlDialog::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + updateView(); + } +#endif +} + void CoinControlDialog::updateView() { if (!model || !model->getOptionsModel() || !model->getAddressTableModel()) diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 6ceb3de61d..3a03341c9e 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -51,6 +51,9 @@ public: static QList<CAmount> payAmounts; static bool fSubtractFeeFromAmount; +protected: + void changeEvent(QEvent* e) override; + private: Ui::CoinControlDialog *ui; CCoinControl& m_coin_control; diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 6d279540e9..f199e8c1a1 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -738,14 +738,14 @@ <item> <widget class="QLabel" name="embeddedFont_label_1"> <property name="text"> - <string>111.11111111 BTC</string> + <string notr="true">111.11111111 BTC</string> </property> </widget> </item> <item> <widget class="QLabel" name="embeddedFont_label_9"> <property name="text"> - <string>909.09090909 BTC</string> + <string notr="true">909.09090909 BTC</string> </property> </widget> </item> @@ -787,14 +787,14 @@ <item> <widget class="QLabel" name="systemFont_label_1"> <property name="text"> - <string>111.11111111 BTC</string> + <string notr="true">111.11111111 BTC</string> </property> </widget> </item> <item> <widget class="QLabel" name="systemFont_label_9"> <property name="text"> - <string>909.09090909 BTC</string> + <string notr="true">909.09090909 BTC</string> </property> </widget> </item> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 0d73ea0ed0..393dca8ccd 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -6,6 +6,7 @@ #include <qt/bitcoinaddressvalidator.h> #include <qt/bitcoinunits.h> +#include <qt/platformstyle.h> #include <qt/qvalidatedlineedit.h> #include <qt/sendcoinsrecipient.h> @@ -56,12 +57,12 @@ #include <QShortcut> #include <QSize> #include <QString> -#include <QStringBuilder> #include <QTextDocument> // for Qt::mightBeRichText #include <QThread> #include <QUrlQuery> #include <QtGlobal> +#include <cassert> #include <chrono> #if defined(Q_OS_MAC) @@ -792,6 +793,40 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m return font_size; } +ThemedLabel::ThemedLabel(const PlatformStyle* platform_style, QWidget* parent) + : QLabel{parent}, m_platform_style{platform_style} +{ + assert(m_platform_style); +} + +void ThemedLabel::setThemedPixmap(const QString& image_filename, int width, int height) +{ + m_image_filename = image_filename; + m_pixmap_width = width; + m_pixmap_height = height; + updateThemedPixmap(); +} + +void ThemedLabel::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + updateThemedPixmap(); + } +#endif + QLabel::changeEvent(e); +} + +void ThemedLabel::updateThemedPixmap() +{ + setPixmap(m_platform_style->SingleColorIcon(m_image_filename).pixmap(m_pixmap_width, m_pixmap_height)); +} + +ClickableLabel::ClickableLabel(const PlatformStyle* platform_style, QWidget* parent) + : ThemedLabel{platform_style, parent} +{ +} + void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) { Q_EMIT clicked(event->pos()); @@ -818,10 +853,12 @@ void PolishProgressDialog(QProgressDialog* dialog) // Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357. const int margin = TextWidth(dialog->fontMetrics(), ("X")); dialog->resize(dialog->width() + 2 * margin, dialog->height()); - dialog->show(); -#else - Q_UNUSED(dialog); #endif + // QProgressDialog estimates the time the operation will take (based on time + // for steps), and only shows itself if that estimate is beyond minimumDuration. + // The default minimumDuration value is 4 seconds, and it could make users + // think that the GUI is frozen. + dialog->setMinimumDuration(0); } int TextWidth(const QFontMetrics& fm, const QString& text) @@ -908,7 +945,7 @@ QString MakeHtmlLink(const QString& source, const QString& link) { return QString(source).replace( link, - QLatin1String("<a href=\"") % link % QLatin1String("\">") % link % QLatin1String("</a>")); + QLatin1String("<a href=\"") + link + QLatin1String("\">") + link + QLatin1String("</a>")); } void PrintSlotException( diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 9c2ad74e1e..06a3b63668 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -27,6 +27,7 @@ #include <chrono> #include <utility> +class PlatformStyle; class QValidatedLineEdit; class SendCoinsRecipient; @@ -231,10 +232,32 @@ namespace GUIUtil qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14); - class ClickableLabel : public QLabel + class ThemedLabel : public QLabel { Q_OBJECT + public: + explicit ThemedLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr); + void setThemedPixmap(const QString& image_filename, int width, int height); + + protected: + void changeEvent(QEvent* e) override; + + private: + const PlatformStyle* m_platform_style; + QString m_image_filename; + int m_pixmap_width; + int m_pixmap_height; + void updateThemedPixmap(); + }; + + class ClickableLabel : public ThemedLabel + { + Q_OBJECT + + public: + explicit ClickableLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr); + Q_SIGNALS: /** Emitted when the label is clicked. The relative mouse coordinates of the click are * passed to the signal. diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 15b14c35ec..a698a96857 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -385,7 +385,9 @@ void Intro::UpdatePruneLabels(bool prune_checked) static constexpr uint64_t nPowTargetSpacing = 10 * 60; // from chainparams, which we don't have at this stage static constexpr uint32_t expected_block_data_size = 2250000; // includes undo data const uint64_t expected_backup_days = m_prune_target_gb * 1e9 / (uint64_t(expected_block_data_size) * 86400 / nPowTargetSpacing); - ui->lblPruneSuffix->setText(tr("(sufficient to restore backups %n day(s) old)", "block chain pruning", expected_backup_days)); + ui->lblPruneSuffix->setText( + //: Explanatory text on the capability of the current prune target. + tr("(sufficient to restore backups %n day(s) old)", "", expected_backup_days)); ui->sizeWarningLabel->setText( tr("%1 will download and store a copy of the Bitcoin block chain.").arg(PACKAGE_NAME) + " " + storageRequiresMsg.arg(m_required_space_gb) + " " + diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index a911f8012e..13a4e3fd79 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -120,20 +120,19 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+3"/> <source>Comma separated file</source> - <comment>Name of CSV file format</comment> + <extracomment>Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+15"/> + <location line="+16"/> <source>There was an error trying to save the address list to %1. Please try again.</source> - <comment>An error message.</comment> - <extracomment>%1 is a name of the file (e.g., "addrbook.csv") that the bitcoin addresses were exported to.</extracomment> + <extracomment>An error message. %1 is a stand-in argument for the name of the file we attempted to save to.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="-2"/> + <location line="-3"/> <source>Exporting Failed</source> <translation type="unfinished"></translation> </message> @@ -318,7 +317,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <context> <name>BitcoinApplication</name> <message> - <location filename="../bitcoin.cpp" line="+421"/> + <location filename="../bitcoin.cpp" line="+420"/> <source>Runaway exception</source> <translation type="unfinished"></translation> </message> @@ -1336,9 +1335,9 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+7"/> + <location line="+9"/> <source>(sufficient to restore backups %n day(s) old)</source> - <comment>block chain pruning</comment> + <extracomment>Explanatory text on the capability of the current prune target.</extracomment> <translation type="unfinished"> <numerusform></numerusform> <numerusform></numerusform> @@ -1355,7 +1354,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-142"/> + <location line="-144"/> <source>Error: Specified data directory "%1" cannot be created.</source> <translation type="unfinished"></translation> </message> @@ -1777,19 +1776,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+22"/> <location line="+49"/> - <source>111.11111111 BTC</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="-42"/> - <location line="+49"/> - <source>909.09090909 BTC</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="-29"/> <source>closest matching "%1"</source> <translation type="unfinished"></translation> </message> @@ -2047,9 +2034,9 @@ Signing is only possible with addresses of the type 'legacy'.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+2"/> <source>Partially Signed Transaction (Binary)</source> - <comment>Name of binary PSBT file format</comment> + <extracomment>Expanded name of the binary PSBT file format. See: BIP 174.</extracomment> <translation type="unfinished"></translation> </message> <message> @@ -2165,43 +2152,51 @@ If you are receiving this error you should request the merchant provide a BIP21 <context> <name>PeerTableModel</name> <message> - <location filename="../peertablemodel.h" line="+77"/> + <location filename="../peertablemodel.h" line="+101"/> <source>User Agent</source> + <extracomment>Title of Peers Table column which contains the peer's User Agent string.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> + <location line="-9"/> <source>Ping</source> + <extracomment>Title of Peers Table column which indicates the current latency of the connection with the peer.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> - <source>Sent</source> + <location line="-12"/> + <source>Peer</source> + <extracomment>Title of Peers Table column which contains a unique number used to identify a connection.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> - <source>Received</source> + <location line="+15"/> + <source>Sent</source> + <extracomment>Title of Peers Table column which indicates the total amount of network information we have sent to the peer.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> - <source>Peer Id</source> + <location line="+3"/> + <source>Received</source> + <extracomment>Title of Peers Table column which indicates the total amount of network information we have received from the peer.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> + <location line="-15"/> <source>Address</source> + <extracomment>Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> + <location line="+3"/> <source>Type</source> + <extracomment>Title of Peers Table column which describes the type of peer connection. The "type" describes why the connection exists.</extracomment> <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> + <location line="+3"/> <source>Network</source> + <extracomment>Title of Peers Table column which states the network the peer connected through.</extracomment> <translation type="unfinished">Network</translation> </message> </context> @@ -2218,7 +2213,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+535"/> + <location line="+540"/> <source>Unroutable</source> <translation type="unfinished"></translation> </message> @@ -2436,9 +2431,9 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+3"/> <source>PNG Image</source> - <comment>Name of PNG file format</comment> + <extracomment>Expanded name of the PNG file format. See https://en.wikipedia.org/wiki/Portable_Network_Graphics</extracomment> <translation type="unfinished"></translation> </message> </context> @@ -2598,7 +2593,7 @@ If you are receiving this error you should request the merchant provide a BIP21 </message> <message> <location line="+68"/> - <location filename="../rpcconsole.cpp" line="+1033"/> + <location filename="../rpcconsole.cpp" line="+1046"/> <source>Select a peer to view detailed information.</source> <translation type="unfinished"></translation> </message> @@ -2844,7 +2839,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location filename="../rpcconsole.cpp" line="-379"/> + <location filename="../rpcconsole.cpp" line="-392"/> <source>Inbound: initiated by peer</source> <translation type="unfinished"></translation> </message> @@ -2889,22 +2884,51 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+327"/> + <location line="+13"/> + <source>Ctrl++</source> + <extracomment>Main shortcut to increase the RPC console font size.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Ctrl+=</source> + <extracomment>Secondary shortcut to increase the RPC console font size.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <source>Ctrl+-</source> + <extracomment>Main shortcut to decrease the RPC console font size.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Ctrl+_</source> + <extracomment>Secondary shortcut to decrease the RPC console font size.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+311"/> <source>Welcome to the %1 RPC console.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+2"/> <source>Use up and down arrows to navigate history, and %1 to clear screen.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+3"/> + <source>Use %1 and %2 to increase or decrease the font size.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> <source>Type %1 for an overview of available commands.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+2"/> <source>For more information on using this console type %1.</source> <translation type="unfinished"></translation> </message> @@ -2924,17 +2948,12 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+117"/> - <source>(peer id: %1)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="-119"/> + <location line="-2"/> <source>Executing command using "%1" wallet</source> <translation type="unfinished"></translation> </message> <message> - <location line="-280"/> + <location line="-283"/> <source>Disconnect</source> <translation type="unfinished"></translation> </message> @@ -2964,7 +2983,12 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+378"/> + <location line="+379"/> + <source>(peer: %1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>via %1</source> <translation type="unfinished"></translation> </message> @@ -3169,7 +3193,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <context> <name>RecentRequestsTableModel</name> <message> - <location filename="../recentrequeststablemodel.cpp" line="+27"/> + <location filename="../recentrequeststablemodel.cpp" line="+30"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -3199,7 +3223,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <translation type="unfinished"></translation> </message> <message> - <location line="+42"/> + <location line="+43"/> <source>Requested</source> <translation type="unfinished"></translation> </message> @@ -3208,7 +3232,7 @@ If you are receiving this error you should request the merchant provide a BIP21 <name>SendCoinsDialog</name> <message> <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+673"/> + <location filename="../sendcoinsdialog.cpp" line="+674"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -3395,7 +3419,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation>S&end</translation> </message> <message> - <location filename="../sendcoinsdialog.cpp" line="-581"/> + <location filename="../sendcoinsdialog.cpp" line="-582"/> <source>Copy quantity</source> <translation type="unfinished"></translation> </message> @@ -3485,12 +3509,18 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+2"/> + <source>Partially Signed Transaction (Binary)</source> + <extracomment>Expanded name of the binary PSBT file format. See: BIP 174.</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> <source>PSBT saved</source> <translation type="unfinished"></translation> </message> <message> - <location line="-75"/> + <location line="-76"/> <source>or</source> <translation type="unfinished"></translation> </message> @@ -3540,13 +3570,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+45"/> - <source>Partially Signed Transaction (Binary)</source> - <comment>Name of binary PSBT file format</comment> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+183"/> + <location line="+229"/> <source>Watch-only balance:</source> <translation type="unfinished"></translation> </message> @@ -3958,7 +3982,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <context> <name>TransactionDesc</name> <message numerus="yes"> - <location filename="../transactiondesc.cpp" line="+34"/> + <location filename="../transactiondesc.cpp" line="+36"/> <source>Open for %n more block(s)</source> <translation> <numerusform>Open for %n more block</numerusform> @@ -3976,7 +4000,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>0/unconfirmed, %1</source> <translation type="unfinished"></translation> </message> @@ -3991,12 +4015,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+0"/> + <location line="-1"/> <source>abandoned</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>%1/unconfirmed</source> <translation type="unfinished"></translation> </message> @@ -4006,7 +4030,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+50"/> + <location line="+51"/> <source>Status</source> <translation type="unfinished"></translation> </message> @@ -4204,7 +4228,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <context> <name>TransactionTableModel</name> <message> - <location filename="../transactiontablemodel.cpp" line="+252"/> + <location filename="../transactiontablemodel.cpp" line="+260"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -4219,7 +4243,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+62"/> + <location line="+60"/> <source>Open for %n more block(s)</source> <translation> <numerusform>Open for %n more block</numerusform> @@ -4451,13 +4475,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+161"/> - <source>Comma separated file</source> - <comment>Name of CSV file format</comment> - <translation type="unfinished"></translation> - </message> - <message> - <location line="-167"/> + <location line="-6"/> <source>Show transaction details</source> <translation type="unfinished"></translation> </message> @@ -4472,7 +4490,13 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+3"/> + <source>Comma separated file</source> + <extracomment>Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</extracomment> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> <source>Confirmed</source> <translation type="unfinished">Confirmed</translation> </message> @@ -4719,9 +4743,9 @@ Go to File > Open Wallet to load a wallet. <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+2"/> <source>Wallet Data</source> - <comment>Name of wallet data file format</comment> + <extracomment>Name of the wallet data file format.</extracomment> <translation type="unfinished"></translation> </message> <message> @@ -4874,16 +4898,6 @@ Go to File > Open Wallet to load a wallet. </message> <message> <location line="+3"/> - <source>SQLiteDatabase: Failed to prepare the statement to fetch sqlite wallet schema version: %s</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> - <source>SQLiteDatabase: Failed to prepare the statement to fetch the application id: %s</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> <source>SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source> <translation type="unfinished"></translation> </message> @@ -5319,16 +5333,6 @@ Go to File > Open Wallet to load a wallet. </message> <message> <location line="+1"/> - <source>SQLiteDatabase: Failed to fetch sqlite wallet schema version: %s</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> - <source>SQLiteDatabase: Failed to fetch the application id: %s</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> <source>SQLiteDatabase: Failed to prepare statement to verify database: %s</source> <translation type="unfinished"></translation> </message> @@ -5424,11 +5428,6 @@ Go to File > Open Wallet to load a wallet. </message> <message> <location line="+1"/> - <source>Transaction fee and change calculation failed</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> <source>Transaction has too long of a mempool chain</source> <translation type="unfinished"></translation> </message> diff --git a/src/qt/locale/bitcoin_en.xlf b/src/qt/locale/bitcoin_en.xlf index 19ba95d999..0f755fc81b 100644 --- a/src/qt/locale/bitcoin_en.xlf +++ b/src/qt/locale/bitcoin_en.xlf @@ -125,20 +125,19 @@ Signing is only possible with addresses of the type 'legacy'.</source> <trans-unit id="_msg24"> <source xml:space="preserve">Comma separated file</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">284</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">Name of CSV file format</context></context-group> + <context-group purpose="location"><context context-type="linenumber">286</context></context-group> + <note annotates="source" from="developer">Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</note> </trans-unit> <trans-unit id="_msg25"> <source xml:space="preserve">There was an error trying to save the address list to %1. Please try again.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">299</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">An error message.</context></context-group> - <note annotates="source" from="developer">%1 is a name of the file (e.g., "addrbook.csv") that the bitcoin addresses were exported to.</note> + <context-group purpose="location"><context context-type="linenumber">302</context></context-group> + <note annotates="source" from="developer">An error message. %1 is a stand-in argument for the name of the file we attempted to save to.</note> </trans-unit> <trans-unit id="_msg26"> <source xml:space="preserve">Exporting Failed</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">297</context></context-group> + <context-group purpose="location"><context context-type="linenumber">299</context></context-group> </trans-unit> </group> </body></file> @@ -331,49 +330,49 @@ Signing is only possible with addresses of the type 'legacy'.</source> <trans-unit id="_msg59"> <source xml:space="preserve">Runaway exception</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">421</context></context-group> + <context-group purpose="location"><context context-type="linenumber">420</context></context-group> </trans-unit> <trans-unit id="_msg60"> <source xml:space="preserve">A fatal error occurred. %1 can no longer continue safely and will quit.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">422</context></context-group> + <context-group purpose="location"><context context-type="linenumber">421</context></context-group> </trans-unit> <trans-unit id="_msg61"> <source xml:space="preserve">Internal error</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">431</context></context-group> + <context-group purpose="location"><context context-type="linenumber">430</context></context-group> </trans-unit> <trans-unit id="_msg62"> <source xml:space="preserve">An internal error occurred. %1 will attempt to continue safely. This is an unexpected bug which can be reported as described below.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">432</context></context-group> + <context-group purpose="location"><context context-type="linenumber">431</context></context-group> </trans-unit> </group> <group restype="x-trolltech-linguist-context" resname="QObject"> <trans-unit id="_msg63"> <source xml:space="preserve">Error: Specified data directory "%1" does not exist.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">543</context></context-group> + <context-group purpose="location"><context context-type="linenumber">542</context></context-group> </trans-unit> <trans-unit id="_msg64"> <source xml:space="preserve">Error: Cannot parse configuration file: %1.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">549</context></context-group> + <context-group purpose="location"><context context-type="linenumber">548</context></context-group> </trans-unit> <trans-unit id="_msg65"> <source xml:space="preserve">Error: %1</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">564</context></context-group> + <context-group purpose="location"><context context-type="linenumber">563</context></context-group> </trans-unit> <trans-unit id="_msg66"> <source xml:space="preserve">Error initializing settings: %1</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">573</context></context-group> + <context-group purpose="location"><context context-type="linenumber">572</context></context-group> </trans-unit> <trans-unit id="_msg67"> <source xml:space="preserve">%1 didn't yet exit safely…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">636</context></context-group> + <context-group purpose="location"><context context-type="linenumber">635</context></context-group> </trans-unit> </group> </body></file> @@ -1380,8 +1379,8 @@ Signing is only possible with addresses of the type 'legacy'.</source> <context-group purpose="location"><context context-type="linenumber">381</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">388</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">block chain pruning</context></context-group> + <context-group purpose="location"><context context-type="linenumber">390</context></context-group> + <note annotates="source" from="developer">Explanatory text on the capability of the current prune target.</note> <trans-unit id="_msg256[0]"> <source xml:space="preserve">(sufficient to restore backups %n day(s) old)</source> <target xml:space="preserve"></target> @@ -1394,12 +1393,12 @@ Signing is only possible with addresses of the type 'legacy'.</source> <trans-unit id="_msg257"> <source xml:space="preserve">%1 will download and store a copy of the Bitcoin block chain.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">390</context></context-group> + <context-group purpose="location"><context context-type="linenumber">392</context></context-group> </trans-unit> <trans-unit id="_msg258"> <source xml:space="preserve">The wallet will also be stored in this directory.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">392</context></context-group> + <context-group purpose="location"><context context-type="linenumber">394</context></context-group> </trans-unit> <trans-unit id="_msg259"> <source xml:space="preserve">Error: Specified data directory "%1" cannot be created.</source> @@ -1905,33 +1904,21 @@ Signing is only possible with addresses of the type 'legacy'.</source> <context-group purpose="location"><context context-type="linenumber">719</context></context-group> </trans-unit> <trans-unit id="_msg352"> - <source xml:space="preserve">111.11111111 BTC</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">741</context></context-group> - <context-group purpose="location"><context context-type="linenumber">790</context></context-group> - </trans-unit> - <trans-unit id="_msg353"> - <source xml:space="preserve">909.09090909 BTC</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">748</context></context-group> - <context-group purpose="location"><context context-type="linenumber">797</context></context-group> - </trans-unit> - <trans-unit id="_msg354"> <source xml:space="preserve">closest matching "%1"</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">768</context></context-group> </trans-unit> - <trans-unit id="_msg355"> + <trans-unit id="_msg353"> <source xml:space="preserve">Options set in this dialog are overridden by the command line or in the configuration file:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">833</context></context-group> </trans-unit> - <trans-unit id="_msg356" approved="yes"> + <trans-unit id="_msg354" approved="yes"> <source xml:space="preserve">&OK</source> <target xml:space="preserve">&OK</target> <context-group purpose="location"><context context-type="linenumber">974</context></context-group> </trans-unit> - <trans-unit id="_msg357" approved="yes"> + <trans-unit id="_msg355" approved="yes"> <source xml:space="preserve">&Cancel</source> <target xml:space="preserve">&Cancel</target> <context-group purpose="location"><context context-type="linenumber">987</context></context-group> @@ -1940,58 +1927,58 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../optionsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OptionsDialog"> - <trans-unit id="_msg358" approved="yes"> + <trans-unit id="_msg356" approved="yes"> <source xml:space="preserve">default</source> <target xml:space="preserve">default</target> <context-group purpose="location"><context context-type="linenumber">104</context></context-group> </trans-unit> - <trans-unit id="_msg359"> + <trans-unit id="_msg357"> <source xml:space="preserve">none</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">185</context></context-group> </trans-unit> - <trans-unit id="_msg360" approved="yes"> + <trans-unit id="_msg358" approved="yes"> <source xml:space="preserve">Confirm options reset</source> <target xml:space="preserve">Confirm options reset</target> <context-group purpose="location"><context context-type="linenumber">276</context></context-group> </trans-unit> - <trans-unit id="_msg361"> + <trans-unit id="_msg359"> <source xml:space="preserve">Client restart required to activate changes.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">277</context></context-group> <context-group purpose="location"><context context-type="linenumber">334</context></context-group> </trans-unit> - <trans-unit id="_msg362"> + <trans-unit id="_msg360"> <source xml:space="preserve">Client will be shut down. Do you want to proceed?</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">277</context></context-group> </trans-unit> - <trans-unit id="_msg363"> + <trans-unit id="_msg361"> <source xml:space="preserve">Configuration options</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">292</context></context-group> </trans-unit> - <trans-unit id="_msg364"> + <trans-unit id="_msg362"> <source xml:space="preserve">The configuration file is used to specify advanced user options which override GUI settings. Additionally, any command-line options will override this configuration file.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">293</context></context-group> </trans-unit> - <trans-unit id="_msg365"> + <trans-unit id="_msg363"> <source xml:space="preserve">Error</source> <target xml:space="preserve" state="needs-review-translation">Error</target> <context-group purpose="location"><context context-type="linenumber">298</context></context-group> </trans-unit> - <trans-unit id="_msg366"> + <trans-unit id="_msg364"> <source xml:space="preserve">The configuration file could not be opened.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">298</context></context-group> </trans-unit> - <trans-unit id="_msg367"> + <trans-unit id="_msg365"> <source xml:space="preserve">This change would require a client restart.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">338</context></context-group> </trans-unit> - <trans-unit id="_msg368" approved="yes"> + <trans-unit id="_msg366" approved="yes"> <source xml:space="preserve">The supplied proxy address is invalid.</source> <target xml:space="preserve">The supplied proxy address is invalid.</target> <context-group purpose="location"><context context-type="linenumber">366</context></context-group> @@ -2000,93 +1987,93 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/overviewpage.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OverviewPage"> - <trans-unit id="_msg369" approved="yes"> + <trans-unit id="_msg367" approved="yes"> <source xml:space="preserve">Form</source> <target xml:space="preserve">Form</target> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg370" approved="yes"> + <trans-unit id="_msg368" approved="yes"> <source xml:space="preserve">The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> <target xml:space="preserve">The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</target> <context-group purpose="location"><context context-type="linenumber">76</context></context-group> <context-group purpose="location"><context context-type="linenumber">411</context></context-group> </trans-unit> - <trans-unit id="_msg371"> + <trans-unit id="_msg369"> <source xml:space="preserve">Watch-only:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">284</context></context-group> </trans-unit> - <trans-unit id="_msg372"> + <trans-unit id="_msg370"> <source xml:space="preserve">Available:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">294</context></context-group> </trans-unit> - <trans-unit id="_msg373" approved="yes"> + <trans-unit id="_msg371" approved="yes"> <source xml:space="preserve">Your current spendable balance</source> <target xml:space="preserve">Your current spendable balance</target> <context-group purpose="location"><context context-type="linenumber">304</context></context-group> </trans-unit> - <trans-unit id="_msg374"> + <trans-unit id="_msg372"> <source xml:space="preserve">Pending:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">339</context></context-group> </trans-unit> - <trans-unit id="_msg375" approved="yes"> + <trans-unit id="_msg373" approved="yes"> <source xml:space="preserve">Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source> <target xml:space="preserve">Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</target> <context-group purpose="location"><context context-type="linenumber">139</context></context-group> </trans-unit> - <trans-unit id="_msg376" approved="yes"> + <trans-unit id="_msg374" approved="yes"> <source xml:space="preserve">Immature:</source> <target xml:space="preserve">Immature:</target> <context-group purpose="location"><context context-type="linenumber">239</context></context-group> </trans-unit> - <trans-unit id="_msg377" approved="yes"> + <trans-unit id="_msg375" approved="yes"> <source xml:space="preserve">Mined balance that has not yet matured</source> <target xml:space="preserve">Mined balance that has not yet matured</target> <context-group purpose="location"><context context-type="linenumber">210</context></context-group> </trans-unit> - <trans-unit id="_msg378"> + <trans-unit id="_msg376"> <source xml:space="preserve">Balances</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">60</context></context-group> </trans-unit> - <trans-unit id="_msg379" approved="yes"> + <trans-unit id="_msg377" approved="yes"> <source xml:space="preserve">Total:</source> <target xml:space="preserve">Total:</target> <context-group purpose="location"><context context-type="linenumber">200</context></context-group> </trans-unit> - <trans-unit id="_msg380" approved="yes"> + <trans-unit id="_msg378" approved="yes"> <source xml:space="preserve">Your current total balance</source> <target xml:space="preserve">Your current total balance</target> <context-group purpose="location"><context context-type="linenumber">249</context></context-group> </trans-unit> - <trans-unit id="_msg381"> + <trans-unit id="_msg379"> <source xml:space="preserve">Your current balance in watch-only addresses</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">323</context></context-group> </trans-unit> - <trans-unit id="_msg382"> + <trans-unit id="_msg380"> <source xml:space="preserve">Spendable:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">346</context></context-group> </trans-unit> - <trans-unit id="_msg383"> + <trans-unit id="_msg381"> <source xml:space="preserve">Recent transactions</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">395</context></context-group> </trans-unit> - <trans-unit id="_msg384"> + <trans-unit id="_msg382"> <source xml:space="preserve">Unconfirmed transactions to watch-only addresses</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> </trans-unit> - <trans-unit id="_msg385"> + <trans-unit id="_msg383"> <source xml:space="preserve">Mined balance in watch-only addresses that has not yet matured</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">158</context></context-group> </trans-unit> - <trans-unit id="_msg386"> + <trans-unit id="_msg384"> <source xml:space="preserve">Current total balance in watch-only addresses</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">268</context></context-group> @@ -2095,7 +2082,7 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../overviewpage.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="OverviewPage"> - <trans-unit id="_msg387"> + <trans-unit id="_msg385"> <source xml:space="preserve">Privacy mode activated for the Overview tab. To unmask the values, uncheck Settings->Mask values.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">191</context></context-group> @@ -2104,32 +2091,32 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../forms/psbtoperationsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog"> - <trans-unit id="_msg388"> + <trans-unit id="_msg386"> <source xml:space="preserve">Dialog</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg389"> + <trans-unit id="_msg387"> <source xml:space="preserve">Sign Tx</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">86</context></context-group> </trans-unit> - <trans-unit id="_msg390"> + <trans-unit id="_msg388"> <source xml:space="preserve">Broadcast Tx</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">102</context></context-group> </trans-unit> - <trans-unit id="_msg391"> + <trans-unit id="_msg389"> <source xml:space="preserve">Copy to Clipboard</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">122</context></context-group> </trans-unit> - <trans-unit id="_msg392"> + <trans-unit id="_msg390"> <source xml:space="preserve">Save…</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">129</context></context-group> </trans-unit> - <trans-unit id="_msg393"> + <trans-unit id="_msg391"> <source xml:space="preserve">Close</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">136</context></context-group> @@ -2138,142 +2125,142 @@ Signing is only possible with addresses of the type 'legacy'.</source> </body></file> <file original="../psbtoperationsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PSBTOperationsDialog"> - <trans-unit id="_msg394"> + <trans-unit id="_msg392"> <source xml:space="preserve">Failed to load transaction: %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">55</context></context-group> </trans-unit> - <trans-unit id="_msg395"> + <trans-unit id="_msg393"> <source xml:space="preserve">Failed to sign transaction: %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">73</context></context-group> </trans-unit> - <trans-unit id="_msg396"> + <trans-unit id="_msg394"> <source xml:space="preserve">Could not sign any more inputs.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">81</context></context-group> </trans-unit> - <trans-unit id="_msg397"> + <trans-unit id="_msg395"> <source xml:space="preserve">Signed %1 inputs, but more signatures are still required.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">83</context></context-group> </trans-unit> - <trans-unit id="_msg398"> + <trans-unit id="_msg396"> <source xml:space="preserve">Signed transaction successfully. Transaction is ready to broadcast.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">86</context></context-group> </trans-unit> - <trans-unit id="_msg399"> + <trans-unit id="_msg397"> <source xml:space="preserve">Unknown error processing transaction.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">98</context></context-group> </trans-unit> - <trans-unit id="_msg400"> + <trans-unit id="_msg398"> <source xml:space="preserve">Transaction broadcast successfully! Transaction ID: %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">108</context></context-group> </trans-unit> - <trans-unit id="_msg401"> + <trans-unit id="_msg399"> <source xml:space="preserve">Transaction broadcast failed: %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">111</context></context-group> </trans-unit> - <trans-unit id="_msg402"> + <trans-unit id="_msg400"> <source xml:space="preserve">PSBT copied to clipboard.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> </trans-unit> - <trans-unit id="_msg403"> + <trans-unit id="_msg401"> <source xml:space="preserve">Save Transaction Data</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">143</context></context-group> </trans-unit> - <trans-unit id="_msg404"> + <trans-unit id="_msg402"> <source xml:space="preserve">Partially Signed Transaction (Binary)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">144</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">Name of binary PSBT file format</context></context-group> + <context-group purpose="location"><context context-type="linenumber">145</context></context-group> + <note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note> </trans-unit> - <trans-unit id="_msg405"> + <trans-unit id="_msg403"> <source xml:space="preserve">PSBT saved to disk.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">151</context></context-group> + <context-group purpose="location"><context context-type="linenumber">152</context></context-group> </trans-unit> - <trans-unit id="_msg406"> + <trans-unit id="_msg404"> <source xml:space="preserve"> * Sends %1 to %2</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">167</context></context-group> + <context-group purpose="location"><context context-type="linenumber">168</context></context-group> </trans-unit> - <trans-unit id="_msg407"> + <trans-unit id="_msg405"> <source xml:space="preserve">Unable to calculate transaction fee or total transaction amount.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">177</context></context-group> + <context-group purpose="location"><context context-type="linenumber">178</context></context-group> </trans-unit> - <trans-unit id="_msg408"> + <trans-unit id="_msg406"> <source xml:space="preserve">Pays transaction fee: </source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">179</context></context-group> + <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> - <trans-unit id="_msg409"> + <trans-unit id="_msg407"> <source xml:space="preserve">Total Amount</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">191</context></context-group> + <context-group purpose="location"><context context-type="linenumber">192</context></context-group> </trans-unit> - <trans-unit id="_msg410"> + <trans-unit id="_msg408"> <source xml:space="preserve">or</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">194</context></context-group> + <context-group purpose="location"><context context-type="linenumber">195</context></context-group> </trans-unit> - <trans-unit id="_msg411"> + <trans-unit id="_msg409"> <source xml:space="preserve">Transaction has %1 unsigned inputs.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">200</context></context-group> + <context-group purpose="location"><context context-type="linenumber">201</context></context-group> </trans-unit> - <trans-unit id="_msg412"> + <trans-unit id="_msg410"> <source xml:space="preserve">Transaction is missing some information about inputs.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">242</context></context-group> + <context-group purpose="location"><context context-type="linenumber">243</context></context-group> </trans-unit> - <trans-unit id="_msg413"> + <trans-unit id="_msg411"> <source xml:space="preserve">Transaction still needs signature(s).</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">246</context></context-group> + <context-group purpose="location"><context context-type="linenumber">247</context></context-group> </trans-unit> - <trans-unit id="_msg414"> + <trans-unit id="_msg412"> <source xml:space="preserve">(But this wallet cannot sign transactions.)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">249</context></context-group> + <context-group purpose="location"><context context-type="linenumber">250</context></context-group> </trans-unit> - <trans-unit id="_msg415"> + <trans-unit id="_msg413"> <source xml:space="preserve">(But this wallet does not have the right keys.)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">252</context></context-group> + <context-group purpose="location"><context context-type="linenumber">253</context></context-group> </trans-unit> - <trans-unit id="_msg416"> + <trans-unit id="_msg414"> <source xml:space="preserve">Transaction is fully signed and ready for broadcast.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">260</context></context-group> + <context-group purpose="location"><context context-type="linenumber">261</context></context-group> </trans-unit> - <trans-unit id="_msg417"> + <trans-unit id="_msg415"> <source xml:space="preserve">Transaction status is unknown.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">264</context></context-group> + <context-group purpose="location"><context context-type="linenumber">265</context></context-group> </trans-unit> </group> </body></file> <file original="../paymentserver.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PaymentServer"> - <trans-unit id="_msg418"> + <trans-unit id="_msg416"> <source xml:space="preserve">Payment request error</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">173</context></context-group> </trans-unit> - <trans-unit id="_msg419"> + <trans-unit id="_msg417"> <source xml:space="preserve">Cannot start bitcoin: click-to-pay handler</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">174</context></context-group> </trans-unit> - <trans-unit id="_msg420"> + <trans-unit id="_msg418"> <source xml:space="preserve">URI handling</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">224</context></context-group> @@ -2281,12 +2268,12 @@ Signing is only possible with addresses of the type 'legacy'.</source> <context-group purpose="location"><context context-type="linenumber">246</context></context-group> <context-group purpose="location"><context context-type="linenumber">253</context></context-group> </trans-unit> - <trans-unit id="_msg421"> + <trans-unit id="_msg419"> <source xml:space="preserve">'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">224</context></context-group> </trans-unit> - <trans-unit id="_msg422"> + <trans-unit id="_msg420"> <source xml:space="preserve">Cannot process payment request because BIP70 is not supported. Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored. If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source> @@ -2294,12 +2281,12 @@ If you are receiving this error you should request the merchant provide a BIP21 <context-group purpose="location"><context context-type="linenumber">241</context></context-group> <context-group purpose="location"><context context-type="linenumber">264</context></context-group> </trans-unit> - <trans-unit id="_msg423"> + <trans-unit id="_msg421"> <source xml:space="preserve">URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">254</context></context-group> </trans-unit> - <trans-unit id="_msg424"> + <trans-unit id="_msg422"> <source xml:space="preserve">Payment request file handling</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">263</context></context-group> @@ -2308,51 +2295,59 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../peertablemodel.h" datatype="c" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="PeerTableModel"> - <trans-unit id="_msg425"> + <trans-unit id="_msg423"> <source xml:space="preserve">User Agent</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">101</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which contains the peer's User Agent string.</note> </trans-unit> - <trans-unit id="_msg426"> + <trans-unit id="_msg424"> <source xml:space="preserve">Ping</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">92</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which indicates the current latency of the connection with the peer.</note> </trans-unit> - <trans-unit id="_msg427"> - <source xml:space="preserve">Sent</source> + <trans-unit id="_msg425"> + <source xml:space="preserve">Peer</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">80</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which contains a unique number used to identify a connection.</note> </trans-unit> - <trans-unit id="_msg428"> - <source xml:space="preserve">Received</source> + <trans-unit id="_msg426"> + <source xml:space="preserve">Sent</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">95</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have sent to the peer.</note> </trans-unit> - <trans-unit id="_msg429"> - <source xml:space="preserve">Peer Id</source> + <trans-unit id="_msg427"> + <source xml:space="preserve">Received</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">98</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which indicates the total amount of network information we have received from the peer.</note> </trans-unit> - <trans-unit id="_msg430"> + <trans-unit id="_msg428"> <source xml:space="preserve">Address</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">83</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer.</note> </trans-unit> - <trans-unit id="_msg431"> + <trans-unit id="_msg429"> <source xml:space="preserve">Type</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">86</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which describes the type of peer connection. The "type" describes why the connection exists.</note> </trans-unit> - <trans-unit id="_msg432"> + <trans-unit id="_msg430"> <source xml:space="preserve">Network</source> <target xml:space="preserve" state="needs-review-translation">Network</target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">89</context></context-group> + <note annotates="source" from="developer">Title of Peers Table column which states the network the peer connected through.</note> </trans-unit> </group> </body></file> <file original="../bitcoinunits.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="QObject"> - <trans-unit id="_msg433"> + <trans-unit id="_msg431"> <source xml:space="preserve">Amount</source> <target xml:space="preserve" state="needs-review-translation">Amount</target> <context-group purpose="location"><context context-type="linenumber">213</context></context-group> @@ -2361,229 +2356,229 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../guiutil.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="QObject"> - <trans-unit id="_msg434"> + <trans-unit id="_msg432"> <source xml:space="preserve">Enter a Bitcoin address (e.g. %1)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">118</context></context-group> </trans-unit> - <trans-unit id="_msg435"> + <trans-unit id="_msg433"> <source xml:space="preserve">Unroutable</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">653</context></context-group> + <context-group purpose="location"><context context-type="linenumber">658</context></context-group> </trans-unit> - <trans-unit id="_msg436"> + <trans-unit id="_msg434"> <source xml:space="preserve">Internal</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">659</context></context-group> + <context-group purpose="location"><context context-type="linenumber">664</context></context-group> </trans-unit> - <trans-unit id="_msg437"> + <trans-unit id="_msg435"> <source xml:space="preserve">Inbound</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">669</context></context-group> + <context-group purpose="location"><context context-type="linenumber">674</context></context-group> </trans-unit> - <trans-unit id="_msg438"> + <trans-unit id="_msg436"> <source xml:space="preserve">Outbound</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">669</context></context-group> + <context-group purpose="location"><context context-type="linenumber">674</context></context-group> </trans-unit> - <trans-unit id="_msg439"> + <trans-unit id="_msg437"> <source xml:space="preserve">Full Relay</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">673</context></context-group> + <context-group purpose="location"><context context-type="linenumber">678</context></context-group> </trans-unit> - <trans-unit id="_msg440"> + <trans-unit id="_msg438"> <source xml:space="preserve">Block Relay</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">674</context></context-group> + <context-group purpose="location"><context context-type="linenumber">679</context></context-group> </trans-unit> - <trans-unit id="_msg441"> + <trans-unit id="_msg439"> <source xml:space="preserve">Manual</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">675</context></context-group> + <context-group purpose="location"><context context-type="linenumber">680</context></context-group> </trans-unit> - <trans-unit id="_msg442"> + <trans-unit id="_msg440"> <source xml:space="preserve">Feeler</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">676</context></context-group> + <context-group purpose="location"><context context-type="linenumber">681</context></context-group> </trans-unit> - <trans-unit id="_msg443"> + <trans-unit id="_msg441"> <source xml:space="preserve">Address Fetch</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">677</context></context-group> + <context-group purpose="location"><context context-type="linenumber">682</context></context-group> </trans-unit> - <trans-unit id="_msg444"> + <trans-unit id="_msg442"> <source xml:space="preserve">%1 d</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">691</context></context-group> + <context-group purpose="location"><context context-type="linenumber">696</context></context-group> </trans-unit> - <trans-unit id="_msg445"> + <trans-unit id="_msg443"> <source xml:space="preserve">%1 h</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">693</context></context-group> + <context-group purpose="location"><context context-type="linenumber">698</context></context-group> </trans-unit> - <trans-unit id="_msg446"> + <trans-unit id="_msg444"> <source xml:space="preserve">%1 m</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">695</context></context-group> + <context-group purpose="location"><context context-type="linenumber">700</context></context-group> </trans-unit> - <trans-unit id="_msg447"> + <trans-unit id="_msg445"> <source xml:space="preserve">%1 s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">697</context></context-group> - <context-group purpose="location"><context context-type="linenumber">725</context></context-group> + <context-group purpose="location"><context context-type="linenumber">702</context></context-group> + <context-group purpose="location"><context context-type="linenumber">730</context></context-group> </trans-unit> - <trans-unit id="_msg448"> + <trans-unit id="_msg446"> <source xml:space="preserve">None</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">713</context></context-group> + <context-group purpose="location"><context context-type="linenumber">718</context></context-group> </trans-unit> - <trans-unit id="_msg449"> + <trans-unit id="_msg447"> <source xml:space="preserve">N/A</source> <target xml:space="preserve" state="needs-review-translation">N/A</target> - <context-group purpose="location"><context context-type="linenumber">719</context></context-group> + <context-group purpose="location"><context context-type="linenumber">724</context></context-group> </trans-unit> - <trans-unit id="_msg450"> + <trans-unit id="_msg448"> <source xml:space="preserve">%1 ms</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">720</context></context-group> + <context-group purpose="location"><context context-type="linenumber">725</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">738</context></context-group> - <trans-unit id="_msg451[0]" approved="yes"> + <context-group purpose="location"><context context-type="linenumber">743</context></context-group> + <trans-unit id="_msg449[0]" approved="yes"> <source xml:space="preserve">%n second(s)</source> <target xml:space="preserve">%n second</target> </trans-unit> - <trans-unit id="_msg451[1]" approved="yes"> + <trans-unit id="_msg449[1]" approved="yes"> <source xml:space="preserve">%n second(s)</source> <target xml:space="preserve">%n seconds</target> </trans-unit> </group> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">742</context></context-group> - <trans-unit id="_msg452[0]" approved="yes"> + <context-group purpose="location"><context context-type="linenumber">747</context></context-group> + <trans-unit id="_msg450[0]" approved="yes"> <source xml:space="preserve">%n minute(s)</source> <target xml:space="preserve">%n minute</target> </trans-unit> - <trans-unit id="_msg452[1]" approved="yes"> + <trans-unit id="_msg450[1]" approved="yes"> <source xml:space="preserve">%n minute(s)</source> <target xml:space="preserve">%n minutes</target> </trans-unit> </group> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">746</context></context-group> - <trans-unit id="_msg453[0]"> + <context-group purpose="location"><context context-type="linenumber">751</context></context-group> + <trans-unit id="_msg451[0]"> <source xml:space="preserve">%n hour(s)</source> <target xml:space="preserve" state="needs-review-translation">%n hour</target> </trans-unit> - <trans-unit id="_msg453[1]"> + <trans-unit id="_msg451[1]"> <source xml:space="preserve">%n hour(s)</source> <target xml:space="preserve" state="needs-review-translation">%n hours</target> </trans-unit> </group> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">750</context></context-group> - <trans-unit id="_msg454[0]"> + <context-group purpose="location"><context context-type="linenumber">755</context></context-group> + <trans-unit id="_msg452[0]"> <source xml:space="preserve">%n day(s)</source> <target xml:space="preserve" state="needs-review-translation">%n day</target> </trans-unit> - <trans-unit id="_msg454[1]"> + <trans-unit id="_msg452[1]"> <source xml:space="preserve">%n day(s)</source> <target xml:space="preserve" state="needs-review-translation">%n days</target> </trans-unit> </group> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">754</context></context-group> - <context-group purpose="location"><context context-type="linenumber">760</context></context-group> - <trans-unit id="_msg455[0]"> + <context-group purpose="location"><context context-type="linenumber">759</context></context-group> + <context-group purpose="location"><context context-type="linenumber">765</context></context-group> + <trans-unit id="_msg453[0]"> <source xml:space="preserve">%n week(s)</source> <target xml:space="preserve" state="needs-review-translation">%n week</target> </trans-unit> - <trans-unit id="_msg455[1]"> + <trans-unit id="_msg453[1]"> <source xml:space="preserve">%n week(s)</source> <target xml:space="preserve" state="needs-review-translation">%n weeks</target> </trans-unit> </group> - <trans-unit id="_msg456"> + <trans-unit id="_msg454"> <source xml:space="preserve">%1 and %2</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">760</context></context-group> + <context-group purpose="location"><context context-type="linenumber">765</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">760</context></context-group> - <trans-unit id="_msg457[0]"> + <context-group purpose="location"><context context-type="linenumber">765</context></context-group> + <trans-unit id="_msg455[0]"> <source xml:space="preserve">%n year(s)</source> <target xml:space="preserve" state="needs-review-translation">%n year</target> </trans-unit> - <trans-unit id="_msg457[1]"> + <trans-unit id="_msg455[1]"> <source xml:space="preserve">%n year(s)</source> <target xml:space="preserve" state="needs-review-translation">%n years</target> </trans-unit> </group> - <trans-unit id="_msg458"> + <trans-unit id="_msg456"> <source xml:space="preserve">%1 B</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">768</context></context-group> + <context-group purpose="location"><context context-type="linenumber">773</context></context-group> </trans-unit> - <trans-unit id="_msg459"> + <trans-unit id="_msg457"> <source xml:space="preserve">%1 kB</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">770</context></context-group> + <context-group purpose="location"><context context-type="linenumber">775</context></context-group> </trans-unit> - <trans-unit id="_msg460"> + <trans-unit id="_msg458"> <source xml:space="preserve">%1 MB</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">772</context></context-group> + <context-group purpose="location"><context context-type="linenumber">777</context></context-group> </trans-unit> - <trans-unit id="_msg461"> + <trans-unit id="_msg459"> <source xml:space="preserve">%1 GB</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">774</context></context-group> + <context-group purpose="location"><context context-type="linenumber">779</context></context-group> </trans-unit> </group> </body></file> <file original="../qrimagewidget.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="QRImageWidget"> - <trans-unit id="_msg462"> + <trans-unit id="_msg460"> <source xml:space="preserve">Save Image…</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">30</context></context-group> </trans-unit> - <trans-unit id="_msg463"> + <trans-unit id="_msg461"> <source xml:space="preserve">Copy Image</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">31</context></context-group> </trans-unit> - <trans-unit id="_msg464"> + <trans-unit id="_msg462"> <source xml:space="preserve">Resulting URI too long, try to reduce the text for label / message.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">42</context></context-group> </trans-unit> - <trans-unit id="_msg465"> + <trans-unit id="_msg463"> <source xml:space="preserve">Error encoding URI into QR Code.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">49</context></context-group> </trans-unit> - <trans-unit id="_msg466"> + <trans-unit id="_msg464"> <source xml:space="preserve">QR code support not available.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">90</context></context-group> </trans-unit> - <trans-unit id="_msg467"> + <trans-unit id="_msg465"> <source xml:space="preserve">Save QR Code</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> </trans-unit> - <trans-unit id="_msg468"> + <trans-unit id="_msg466"> <source xml:space="preserve">PNG Image</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">121</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">Name of PNG file format</context></context-group> + <context-group purpose="location"><context context-type="linenumber">123</context></context-group> + <note annotates="source" from="developer">Expanded name of the PNG file format. See https://en.wikipedia.org/wiki/Portable_Network_Graphics</note> </trans-unit> </group> </body></file> <file original="../forms/debugwindow.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RPCConsole"> - <trans-unit id="_msg469" approved="yes"> + <trans-unit id="_msg467" approved="yes"> <source xml:space="preserve">N/A</source> <target xml:space="preserve">N/A</target> <context-group purpose="location"><context context-type="linenumber">75</context></context-group> @@ -2622,322 +2617,322 @@ If you are receiving this error you should request the merchant provide a BIP21 <context-group purpose="location"><context context-type="linenumber">1614</context></context-group> <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.h</context><context context-type="linenumber">137</context></context-group> </trans-unit> - <trans-unit id="_msg470" approved="yes"> + <trans-unit id="_msg468" approved="yes"> <source xml:space="preserve">Client version</source> <target xml:space="preserve">Client version</target> <context-group purpose="location"><context context-type="linenumber">65</context></context-group> </trans-unit> - <trans-unit id="_msg471" approved="yes"> + <trans-unit id="_msg469" approved="yes"> <source xml:space="preserve">&Information</source> <target xml:space="preserve">&Information</target> <context-group purpose="location"><context context-type="linenumber">43</context></context-group> </trans-unit> - <trans-unit id="_msg472"> + <trans-unit id="_msg470"> <source xml:space="preserve">General</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> </trans-unit> - <trans-unit id="_msg473"> + <trans-unit id="_msg471"> <source xml:space="preserve">Datadir</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">114</context></context-group> </trans-unit> - <trans-unit id="_msg474"> + <trans-unit id="_msg472"> <source xml:space="preserve">To specify a non-default location of the data directory use the '%1' option.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">124</context></context-group> </trans-unit> - <trans-unit id="_msg475"> + <trans-unit id="_msg473"> <source xml:space="preserve">Blocksdir</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">143</context></context-group> </trans-unit> - <trans-unit id="_msg476"> + <trans-unit id="_msg474"> <source xml:space="preserve">To specify a non-default location of the blocks directory use the '%1' option.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">153</context></context-group> </trans-unit> - <trans-unit id="_msg477" approved="yes"> + <trans-unit id="_msg475" approved="yes"> <source xml:space="preserve">Startup time</source> <target xml:space="preserve">Startup time</target> <context-group purpose="location"><context context-type="linenumber">172</context></context-group> </trans-unit> - <trans-unit id="_msg478" approved="yes"> + <trans-unit id="_msg476" approved="yes"> <source xml:space="preserve">Network</source> <target xml:space="preserve">Network</target> <context-group purpose="location"><context context-type="linenumber">201</context></context-group> <context-group purpose="location"><context context-type="linenumber">1123</context></context-group> </trans-unit> - <trans-unit id="_msg479"> + <trans-unit id="_msg477"> <source xml:space="preserve">Name</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">208</context></context-group> </trans-unit> - <trans-unit id="_msg480" approved="yes"> + <trans-unit id="_msg478" approved="yes"> <source xml:space="preserve">Number of connections</source> <target xml:space="preserve">Number of connections</target> <context-group purpose="location"><context context-type="linenumber">231</context></context-group> </trans-unit> - <trans-unit id="_msg481" approved="yes"> + <trans-unit id="_msg479" approved="yes"> <source xml:space="preserve">Block chain</source> <target xml:space="preserve">Block chain</target> <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> - <trans-unit id="_msg482"> + <trans-unit id="_msg480"> <source xml:space="preserve">Memory Pool</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">319</context></context-group> </trans-unit> - <trans-unit id="_msg483"> + <trans-unit id="_msg481"> <source xml:space="preserve">Current number of transactions</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">326</context></context-group> </trans-unit> - <trans-unit id="_msg484"> + <trans-unit id="_msg482"> <source xml:space="preserve">Memory usage</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">349</context></context-group> </trans-unit> - <trans-unit id="_msg485"> + <trans-unit id="_msg483"> <source xml:space="preserve">Wallet: </source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">443</context></context-group> </trans-unit> - <trans-unit id="_msg486"> + <trans-unit id="_msg484"> <source xml:space="preserve">(none)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">454</context></context-group> </trans-unit> - <trans-unit id="_msg487"> + <trans-unit id="_msg485"> <source xml:space="preserve">&Reset</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">695</context></context-group> </trans-unit> - <trans-unit id="_msg488"> + <trans-unit id="_msg486"> <source xml:space="preserve">Received</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">775</context></context-group> <context-group purpose="location"><context context-type="linenumber">1483</context></context-group> </trans-unit> - <trans-unit id="_msg489"> + <trans-unit id="_msg487"> <source xml:space="preserve">Sent</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">855</context></context-group> <context-group purpose="location"><context context-type="linenumber">1460</context></context-group> </trans-unit> - <trans-unit id="_msg490"> + <trans-unit id="_msg488"> <source xml:space="preserve">&Peers</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">896</context></context-group> </trans-unit> - <trans-unit id="_msg491"> + <trans-unit id="_msg489"> <source xml:space="preserve">Banned peers</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">972</context></context-group> </trans-unit> - <trans-unit id="_msg492"> + <trans-unit id="_msg490"> <source xml:space="preserve">Select a peer to view detailed information.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1040</context></context-group> - <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1033</context></context-group> + <context-group purpose="location"><context context-type="sourcefile">../rpcconsole.cpp</context><context context-type="linenumber">1046</context></context-group> </trans-unit> - <trans-unit id="_msg493"> + <trans-unit id="_msg491"> <source xml:space="preserve">Version</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1146</context></context-group> </trans-unit> - <trans-unit id="_msg494"> + <trans-unit id="_msg492"> <source xml:space="preserve">Starting Block</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1270</context></context-group> </trans-unit> - <trans-unit id="_msg495"> + <trans-unit id="_msg493"> <source xml:space="preserve">Synced Headers</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1293</context></context-group> </trans-unit> - <trans-unit id="_msg496"> + <trans-unit id="_msg494"> <source xml:space="preserve">Synced Blocks</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1316</context></context-group> </trans-unit> - <trans-unit id="_msg497"> + <trans-unit id="_msg495"> <source xml:space="preserve">The mapped Autonomous System used for diversifying peer selection.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1601</context></context-group> </trans-unit> - <trans-unit id="_msg498"> + <trans-unit id="_msg496"> <source xml:space="preserve">Mapped AS</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1604</context></context-group> </trans-unit> - <trans-unit id="_msg499"> + <trans-unit id="_msg497"> <source xml:space="preserve">User Agent</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">88</context></context-group> <context-group purpose="location"><context context-type="linenumber">1169</context></context-group> </trans-unit> - <trans-unit id="_msg500"> + <trans-unit id="_msg498"> <source xml:space="preserve">Node window</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg501"> + <trans-unit id="_msg499"> <source xml:space="preserve">Current block height</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">267</context></context-group> </trans-unit> - <trans-unit id="_msg502"> + <trans-unit id="_msg500"> <source xml:space="preserve">Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">397</context></context-group> </trans-unit> - <trans-unit id="_msg503"> + <trans-unit id="_msg501"> <source xml:space="preserve">Decrease font size</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">481</context></context-group> </trans-unit> - <trans-unit id="_msg504"> + <trans-unit id="_msg502"> <source xml:space="preserve">Increase font size</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">513</context></context-group> </trans-unit> - <trans-unit id="_msg505"> + <trans-unit id="_msg503"> <source xml:space="preserve">Permissions</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1071</context></context-group> </trans-unit> - <trans-unit id="_msg506"> + <trans-unit id="_msg504"> <source xml:space="preserve">The direction and type of peer connection: %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1094</context></context-group> </trans-unit> - <trans-unit id="_msg507"> + <trans-unit id="_msg505"> <source xml:space="preserve">Direction/Type</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1097</context></context-group> </trans-unit> - <trans-unit id="_msg508"> + <trans-unit id="_msg506"> <source xml:space="preserve">The network protocol this peer is connected through: IPv4, IPv6, Onion, I2P, or CJDNS.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1120</context></context-group> </trans-unit> - <trans-unit id="_msg509"> + <trans-unit id="_msg507"> <source xml:space="preserve">Services</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1192</context></context-group> </trans-unit> - <trans-unit id="_msg510"> + <trans-unit id="_msg508"> <source xml:space="preserve">Whether the peer requested us to relay transactions.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1218</context></context-group> </trans-unit> - <trans-unit id="_msg511"> + <trans-unit id="_msg509"> <source xml:space="preserve">Wants Tx Relay</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1221</context></context-group> </trans-unit> - <trans-unit id="_msg512"> + <trans-unit id="_msg510"> <source xml:space="preserve">High bandwidth BIP152 compact block relay: %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1244</context></context-group> </trans-unit> - <trans-unit id="_msg513"> + <trans-unit id="_msg511"> <source xml:space="preserve">High Bandwidth</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1247</context></context-group> </trans-unit> - <trans-unit id="_msg514"> + <trans-unit id="_msg512"> <source xml:space="preserve">Connection Time</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1339</context></context-group> </trans-unit> - <trans-unit id="_msg515"> + <trans-unit id="_msg513"> <source xml:space="preserve">Elapsed time since a novel block passing initial validity checks was received from this peer.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1362</context></context-group> </trans-unit> - <trans-unit id="_msg516"> + <trans-unit id="_msg514"> <source xml:space="preserve">Last Block</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1365</context></context-group> </trans-unit> - <trans-unit id="_msg517"> + <trans-unit id="_msg515"> <source xml:space="preserve">Elapsed time since a novel transaction accepted into our mempool was received from this peer.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1388</context></context-group> </trans-unit> - <trans-unit id="_msg518"> + <trans-unit id="_msg516"> <source xml:space="preserve">Last Tx</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1391</context></context-group> </trans-unit> - <trans-unit id="_msg519"> + <trans-unit id="_msg517"> <source xml:space="preserve">Last Send</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1414</context></context-group> </trans-unit> - <trans-unit id="_msg520"> + <trans-unit id="_msg518"> <source xml:space="preserve">Last Receive</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1437</context></context-group> </trans-unit> - <trans-unit id="_msg521"> + <trans-unit id="_msg519"> <source xml:space="preserve">Ping Time</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1506</context></context-group> </trans-unit> - <trans-unit id="_msg522"> + <trans-unit id="_msg520"> <source xml:space="preserve">The duration of a currently outstanding ping.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1529</context></context-group> </trans-unit> - <trans-unit id="_msg523"> + <trans-unit id="_msg521"> <source xml:space="preserve">Ping Wait</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1532</context></context-group> </trans-unit> - <trans-unit id="_msg524"> + <trans-unit id="_msg522"> <source xml:space="preserve">Min Ping</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1555</context></context-group> </trans-unit> - <trans-unit id="_msg525"> + <trans-unit id="_msg523"> <source xml:space="preserve">Time Offset</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1578</context></context-group> </trans-unit> - <trans-unit id="_msg526" approved="yes"> + <trans-unit id="_msg524" approved="yes"> <source xml:space="preserve">Last block time</source> <target xml:space="preserve">Last block time</target> <context-group purpose="location"><context context-type="linenumber">290</context></context-group> </trans-unit> - <trans-unit id="_msg527" approved="yes"> + <trans-unit id="_msg525" approved="yes"> <source xml:space="preserve">&Open</source> <target xml:space="preserve">&Open</target> <context-group purpose="location"><context context-type="linenumber">400</context></context-group> </trans-unit> - <trans-unit id="_msg528" approved="yes"> + <trans-unit id="_msg526" approved="yes"> <source xml:space="preserve">&Console</source> <target xml:space="preserve">&Console</target> <context-group purpose="location"><context context-type="linenumber">426</context></context-group> </trans-unit> - <trans-unit id="_msg529"> + <trans-unit id="_msg527"> <source xml:space="preserve">&Network Traffic</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">643</context></context-group> </trans-unit> - <trans-unit id="_msg530"> + <trans-unit id="_msg528"> <source xml:space="preserve">Totals</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">711</context></context-group> </trans-unit> - <trans-unit id="_msg531" approved="yes"> + <trans-unit id="_msg529" approved="yes"> <source xml:space="preserve">Debug log file</source> <target xml:space="preserve">Debug log file</target> <context-group purpose="location"><context context-type="linenumber">390</context></context-group> </trans-unit> - <trans-unit id="_msg532" approved="yes"> + <trans-unit id="_msg530" approved="yes"> <source xml:space="preserve">Clear console</source> <target xml:space="preserve">Clear console</target> <context-group purpose="location"><context context-type="linenumber">545</context></context-group> @@ -2946,176 +2941,205 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../rpcconsole.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RPCConsole"> - <trans-unit id="_msg533"> + <trans-unit id="_msg531"> <source xml:space="preserve">In:</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">852</context></context-group> + <context-group purpose="location"><context context-type="linenumber">865</context></context-group> </trans-unit> - <trans-unit id="_msg534"> + <trans-unit id="_msg532"> <source xml:space="preserve">Out:</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">853</context></context-group> + <context-group purpose="location"><context context-type="linenumber">866</context></context-group> </trans-unit> - <trans-unit id="_msg535"> + <trans-unit id="_msg533"> <source xml:space="preserve">Inbound: initiated by peer</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">474</context></context-group> </trans-unit> - <trans-unit id="_msg536"> + <trans-unit id="_msg534"> <source xml:space="preserve">Outbound Full Relay: default</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">475</context></context-group> </trans-unit> - <trans-unit id="_msg537"> + <trans-unit id="_msg535"> <source xml:space="preserve">Outbound Block Relay: does not relay transactions or addresses</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">476</context></context-group> </trans-unit> - <trans-unit id="_msg538"> + <trans-unit id="_msg536"> <source xml:space="preserve">Outbound Manual: added using RPC %1 or %2/%3 configuration options</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">477</context></context-group> </trans-unit> - <trans-unit id="_msg539"> + <trans-unit id="_msg537"> <source xml:space="preserve">Outbound Feeler: short-lived, for testing addresses</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">481</context></context-group> </trans-unit> - <trans-unit id="_msg540"> + <trans-unit id="_msg538"> <source xml:space="preserve">Outbound Address Fetch: short-lived, for soliciting addresses</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">482</context></context-group> </trans-unit> - <trans-unit id="_msg541"> + <trans-unit id="_msg539"> <source xml:space="preserve">we selected the peer for high bandwidth relay</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">486</context></context-group> </trans-unit> - <trans-unit id="_msg542"> + <trans-unit id="_msg540"> <source xml:space="preserve">the peer selected us for high bandwidth relay</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">487</context></context-group> </trans-unit> - <trans-unit id="_msg543"> + <trans-unit id="_msg541"> <source xml:space="preserve">no high bandwidth relay selected</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">488</context></context-group> </trans-unit> + <trans-unit id="_msg542"> + <source xml:space="preserve">Ctrl++</source> + <target xml:space="preserve"></target> + <context-group purpose="location"><context context-type="linenumber">501</context></context-group> + <note annotates="source" from="developer">Main shortcut to increase the RPC console font size.</note> + </trans-unit> + <trans-unit id="_msg543"> + <source xml:space="preserve">Ctrl+=</source> + <target xml:space="preserve"></target> + <context-group purpose="location"><context context-type="linenumber">503</context></context-group> + <note annotates="source" from="developer">Secondary shortcut to increase the RPC console font size.</note> + </trans-unit> <trans-unit id="_msg544"> - <source xml:space="preserve">Welcome to the %1 RPC console.</source> + <source xml:space="preserve">Ctrl+-</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">815</context></context-group> + <context-group purpose="location"><context context-type="linenumber">507</context></context-group> + <note annotates="source" from="developer">Main shortcut to decrease the RPC console font size.</note> </trans-unit> <trans-unit id="_msg545"> - <source xml:space="preserve">Use up and down arrows to navigate history, and %1 to clear screen.</source> + <source xml:space="preserve">Ctrl+_</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">816</context></context-group> + <context-group purpose="location"><context context-type="linenumber">509</context></context-group> + <note annotates="source" from="developer">Secondary shortcut to decrease the RPC console font size.</note> </trans-unit> <trans-unit id="_msg546"> - <source xml:space="preserve">Type %1 for an overview of available commands.</source> + <source xml:space="preserve">Welcome to the %1 RPC console.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">817</context></context-group> + <context-group purpose="location"><context context-type="linenumber">820</context></context-group> </trans-unit> <trans-unit id="_msg547"> - <source xml:space="preserve">For more information on using this console type %1.</source> + <source xml:space="preserve">Use up and down arrows to navigate history, and %1 to clear screen.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">818</context></context-group> + <context-group purpose="location"><context context-type="linenumber">822</context></context-group> </trans-unit> <trans-unit id="_msg548"> - <source xml:space="preserve">WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.</source> + <source xml:space="preserve">Use %1 and %2 to increase or decrease the font size.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">820</context></context-group> + <context-group purpose="location"><context context-type="linenumber">825</context></context-group> </trans-unit> <trans-unit id="_msg549"> - <source xml:space="preserve">Network activity disabled</source> + <source xml:space="preserve">Type %1 for an overview of available commands.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">856</context></context-group> + <context-group purpose="location"><context context-type="linenumber">829</context></context-group> </trans-unit> <trans-unit id="_msg550"> - <source xml:space="preserve">Executing command without any wallet</source> + <source xml:space="preserve">For more information on using this console type %1.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">922</context></context-group> + <context-group purpose="location"><context context-type="linenumber">831</context></context-group> </trans-unit> <trans-unit id="_msg551"> - <source xml:space="preserve">(peer id: %1)</source> + <source xml:space="preserve">WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">1039</context></context-group> + <context-group purpose="location"><context context-type="linenumber">833</context></context-group> </trans-unit> <trans-unit id="_msg552"> - <source xml:space="preserve">Executing command using "%1" wallet</source> + <source xml:space="preserve">Network activity disabled</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">920</context></context-group> + <context-group purpose="location"><context context-type="linenumber">869</context></context-group> </trans-unit> <trans-unit id="_msg553"> - <source xml:space="preserve">Disconnect</source> + <source xml:space="preserve">Executing command without any wallet</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">640</context></context-group> + <context-group purpose="location"><context context-type="linenumber">935</context></context-group> </trans-unit> <trans-unit id="_msg554"> - <source xml:space="preserve">1 hour</source> + <source xml:space="preserve">Executing command using "%1" wallet</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">641</context></context-group> + <context-group purpose="location"><context context-type="linenumber">933</context></context-group> </trans-unit> <trans-unit id="_msg555"> - <source xml:space="preserve">1 day</source> + <source xml:space="preserve">Disconnect</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">642</context></context-group> + <context-group purpose="location"><context context-type="linenumber">650</context></context-group> </trans-unit> <trans-unit id="_msg556"> - <source xml:space="preserve">1 week</source> + <source xml:space="preserve">1 hour</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">643</context></context-group> + <context-group purpose="location"><context context-type="linenumber">651</context></context-group> </trans-unit> <trans-unit id="_msg557"> - <source xml:space="preserve">1 year</source> + <source xml:space="preserve">1 day</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">644</context></context-group> + <context-group purpose="location"><context context-type="linenumber">652</context></context-group> </trans-unit> <trans-unit id="_msg558"> - <source xml:space="preserve">Unban</source> + <source xml:space="preserve">1 week</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">663</context></context-group> + <context-group purpose="location"><context context-type="linenumber">653</context></context-group> </trans-unit> <trans-unit id="_msg559"> + <source xml:space="preserve">1 year</source> + <target xml:space="preserve"></target> + <context-group purpose="location"><context context-type="linenumber">654</context></context-group> + </trans-unit> + <trans-unit id="_msg560"> + <source xml:space="preserve">Unban</source> + <target xml:space="preserve"></target> + <context-group purpose="location"><context context-type="linenumber">673</context></context-group> + </trans-unit> + <trans-unit id="_msg561"> + <source xml:space="preserve">(peer: %1)</source> + <target xml:space="preserve"></target> + <context-group purpose="location"><context context-type="linenumber">1052</context></context-group> + </trans-unit> + <trans-unit id="_msg562"> <source xml:space="preserve">via %1</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">1041</context></context-group> + <context-group purpose="location"><context context-type="linenumber">1054</context></context-group> </trans-unit> </group> </body></file> <file original="../rpcconsole.h" datatype="c" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RPCConsole"> - <trans-unit id="_msg560"> + <trans-unit id="_msg563"> <source xml:space="preserve">Yes</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">136</context></context-group> </trans-unit> - <trans-unit id="_msg561"> + <trans-unit id="_msg564"> <source xml:space="preserve">No</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">136</context></context-group> </trans-unit> - <trans-unit id="_msg562"> + <trans-unit id="_msg565"> <source xml:space="preserve">To</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">136</context></context-group> </trans-unit> - <trans-unit id="_msg563"> + <trans-unit id="_msg566"> <source xml:space="preserve">From</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">136</context></context-group> </trans-unit> - <trans-unit id="_msg564"> + <trans-unit id="_msg567"> <source xml:space="preserve">Ban for</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">137</context></context-group> </trans-unit> - <trans-unit id="_msg565"> + <trans-unit id="_msg568"> <source xml:space="preserve">Never</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">174</context></context-group> </trans-unit> - <trans-unit id="_msg566"> + <trans-unit id="_msg569"> <source xml:space="preserve">Unknown</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">137</context></context-group> @@ -3124,98 +3148,98 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../forms/receivecoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog"> - <trans-unit id="_msg567"> + <trans-unit id="_msg570"> <source xml:space="preserve">&Amount:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">37</context></context-group> </trans-unit> - <trans-unit id="_msg568"> + <trans-unit id="_msg571"> <source xml:space="preserve">&Label:</source> <target xml:space="preserve" state="needs-review-translation">&Label:</target> <context-group purpose="location"><context context-type="linenumber">83</context></context-group> </trans-unit> - <trans-unit id="_msg569"> + <trans-unit id="_msg572"> <source xml:space="preserve">&Message:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">53</context></context-group> </trans-unit> - <trans-unit id="_msg570"> + <trans-unit id="_msg573"> <source xml:space="preserve">An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">50</context></context-group> </trans-unit> - <trans-unit id="_msg571"> + <trans-unit id="_msg574"> <source xml:space="preserve">An optional label to associate with the new receiving address.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> - <trans-unit id="_msg572"> + <trans-unit id="_msg575"> <source xml:space="preserve">Use this form to request payments. All fields are <b>optional</b>.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">73</context></context-group> </trans-unit> - <trans-unit id="_msg573"> + <trans-unit id="_msg576"> <source xml:space="preserve">An optional amount to request. Leave this empty or zero to not request a specific amount.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">34</context></context-group> <context-group purpose="location"><context context-type="linenumber">193</context></context-group> </trans-unit> - <trans-unit id="_msg574"> + <trans-unit id="_msg577"> <source xml:space="preserve">An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">66</context></context-group> </trans-unit> - <trans-unit id="_msg575"> + <trans-unit id="_msg578"> <source xml:space="preserve">An optional message that is attached to the payment request and may be displayed to the sender.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> </trans-unit> - <trans-unit id="_msg576"> + <trans-unit id="_msg579"> <source xml:space="preserve">&Create new receiving address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">111</context></context-group> </trans-unit> - <trans-unit id="_msg577"> + <trans-unit id="_msg580"> <source xml:space="preserve">Clear all fields of the form.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">134</context></context-group> </trans-unit> - <trans-unit id="_msg578"> + <trans-unit id="_msg581"> <source xml:space="preserve">Clear</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">137</context></context-group> </trans-unit> - <trans-unit id="_msg579"> + <trans-unit id="_msg582"> <source xml:space="preserve">Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">215</context></context-group> </trans-unit> - <trans-unit id="_msg580"> + <trans-unit id="_msg583"> <source xml:space="preserve">Generate native segwit (Bech32) address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">218</context></context-group> </trans-unit> - <trans-unit id="_msg581"> + <trans-unit id="_msg584"> <source xml:space="preserve">Requested payments history</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">279</context></context-group> </trans-unit> - <trans-unit id="_msg582"> + <trans-unit id="_msg585"> <source xml:space="preserve">Show the selected request (does the same as double clicking an entry)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">304</context></context-group> </trans-unit> - <trans-unit id="_msg583"> + <trans-unit id="_msg586"> <source xml:space="preserve">Show</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">307</context></context-group> </trans-unit> - <trans-unit id="_msg584"> + <trans-unit id="_msg587"> <source xml:space="preserve">Remove the selected entries from the list</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">324</context></context-group> </trans-unit> - <trans-unit id="_msg585"> + <trans-unit id="_msg588"> <source xml:space="preserve">Remove</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">327</context></context-group> @@ -3224,37 +3248,37 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../receivecoinsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveCoinsDialog"> - <trans-unit id="_msg586"> + <trans-unit id="_msg589"> <source xml:space="preserve">Copy URI</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">47</context></context-group> </trans-unit> - <trans-unit id="_msg587"> + <trans-unit id="_msg590"> <source xml:space="preserve">Copy address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">48</context></context-group> </trans-unit> - <trans-unit id="_msg588"> + <trans-unit id="_msg591"> <source xml:space="preserve">Copy label</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">49</context></context-group> </trans-unit> - <trans-unit id="_msg589"> + <trans-unit id="_msg592"> <source xml:space="preserve">Copy message</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">50</context></context-group> </trans-unit> - <trans-unit id="_msg590"> + <trans-unit id="_msg593"> <source xml:space="preserve">Copy amount</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">51</context></context-group> </trans-unit> - <trans-unit id="_msg591"> + <trans-unit id="_msg594"> <source xml:space="preserve">Could not unlock wallet.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">176</context></context-group> </trans-unit> - <trans-unit id="_msg592"> + <trans-unit id="_msg595"> <source xml:space="preserve">Could not generate new %1 address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">181</context></context-group> @@ -3263,52 +3287,52 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../forms/receiverequestdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog"> - <trans-unit id="_msg593"> + <trans-unit id="_msg596"> <source xml:space="preserve">Request payment to …</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg594"> + <trans-unit id="_msg597"> <source xml:space="preserve">Address:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">90</context></context-group> </trans-unit> - <trans-unit id="_msg595"> + <trans-unit id="_msg598"> <source xml:space="preserve">Amount:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">119</context></context-group> </trans-unit> - <trans-unit id="_msg596"> + <trans-unit id="_msg599"> <source xml:space="preserve">Label:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">148</context></context-group> </trans-unit> - <trans-unit id="_msg597"> + <trans-unit id="_msg600"> <source xml:space="preserve">Message:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> - <trans-unit id="_msg598"> + <trans-unit id="_msg601"> <source xml:space="preserve">Wallet:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">212</context></context-group> </trans-unit> - <trans-unit id="_msg599"> + <trans-unit id="_msg602"> <source xml:space="preserve">Copy &URI</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">240</context></context-group> </trans-unit> - <trans-unit id="_msg600"> + <trans-unit id="_msg603"> <source xml:space="preserve">Copy &Address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">250</context></context-group> </trans-unit> - <trans-unit id="_msg601"> + <trans-unit id="_msg604"> <source xml:space="preserve">&Save Image…</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> - <trans-unit id="_msg602"> + <trans-unit id="_msg605"> <source xml:space="preserve">Payment information</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">39</context></context-group> @@ -3317,7 +3341,7 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../receiverequestdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="ReceiveRequestDialog"> - <trans-unit id="_msg603"> + <trans-unit id="_msg606"> <source xml:space="preserve">Request payment to %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">49</context></context-group> @@ -3326,229 +3350,229 @@ If you are receiving this error you should request the merchant provide a BIP21 </body></file> <file original="../recentrequeststablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="RecentRequestsTableModel"> - <trans-unit id="_msg604"> + <trans-unit id="_msg607"> <source xml:space="preserve">Date</source> <target xml:space="preserve" state="needs-review-translation">Date</target> - <context-group purpose="location"><context context-type="linenumber">27</context></context-group> + <context-group purpose="location"><context context-type="linenumber">30</context></context-group> </trans-unit> - <trans-unit id="_msg605"> + <trans-unit id="_msg608"> <source xml:space="preserve">Label</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">27</context></context-group> + <context-group purpose="location"><context context-type="linenumber">30</context></context-group> </trans-unit> - <trans-unit id="_msg606"> + <trans-unit id="_msg609"> <source xml:space="preserve">Message</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">27</context></context-group> + <context-group purpose="location"><context context-type="linenumber">30</context></context-group> </trans-unit> - <trans-unit id="_msg607"> + <trans-unit id="_msg610"> <source xml:space="preserve">(no label)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">68</context></context-group> + <context-group purpose="location"><context context-type="linenumber">71</context></context-group> </trans-unit> - <trans-unit id="_msg608"> + <trans-unit id="_msg611"> <source xml:space="preserve">(no message)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> + <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> - <trans-unit id="_msg609"> + <trans-unit id="_msg612"> <source xml:space="preserve">(no amount requested)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">85</context></context-group> + <context-group purpose="location"><context context-type="linenumber">88</context></context-group> </trans-unit> - <trans-unit id="_msg610"> + <trans-unit id="_msg613"> <source xml:space="preserve">Requested</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">127</context></context-group> + <context-group purpose="location"><context context-type="linenumber">131</context></context-group> </trans-unit> </group> </body></file> <file original="../forms/sendcoinsdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SendCoinsDialog"> - <trans-unit id="_msg611" approved="yes"> + <trans-unit id="_msg614" approved="yes"> <source xml:space="preserve">Send Coins</source> <target xml:space="preserve">Send Coins</target> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> - <context-group purpose="location"><context context-type="sourcefile">../sendcoinsdialog.cpp</context><context context-type="linenumber">673</context></context-group> + <context-group purpose="location"><context context-type="sourcefile">../sendcoinsdialog.cpp</context><context context-type="linenumber">674</context></context-group> </trans-unit> - <trans-unit id="_msg612"> + <trans-unit id="_msg615"> <source xml:space="preserve">Coin Control Features</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">90</context></context-group> </trans-unit> - <trans-unit id="_msg613"> + <trans-unit id="_msg616"> <source xml:space="preserve">automatically selected</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> </trans-unit> - <trans-unit id="_msg614"> + <trans-unit id="_msg617"> <source xml:space="preserve">Insufficient funds!</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">139</context></context-group> </trans-unit> - <trans-unit id="_msg615"> + <trans-unit id="_msg618"> <source xml:space="preserve">Quantity:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">228</context></context-group> </trans-unit> - <trans-unit id="_msg616"> + <trans-unit id="_msg619"> <source xml:space="preserve">Bytes:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">263</context></context-group> </trans-unit> - <trans-unit id="_msg617"> + <trans-unit id="_msg620"> <source xml:space="preserve">Amount:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">311</context></context-group> </trans-unit> - <trans-unit id="_msg618"> + <trans-unit id="_msg621"> <source xml:space="preserve">Fee:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">391</context></context-group> </trans-unit> - <trans-unit id="_msg619"> + <trans-unit id="_msg622"> <source xml:space="preserve">After Fee:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">442</context></context-group> </trans-unit> - <trans-unit id="_msg620"> + <trans-unit id="_msg623"> <source xml:space="preserve">Change:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">474</context></context-group> </trans-unit> - <trans-unit id="_msg621"> + <trans-unit id="_msg624"> <source xml:space="preserve">If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">518</context></context-group> </trans-unit> - <trans-unit id="_msg622"> + <trans-unit id="_msg625"> <source xml:space="preserve">Custom change address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">521</context></context-group> </trans-unit> - <trans-unit id="_msg623"> + <trans-unit id="_msg626"> <source xml:space="preserve">Transaction Fee:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">727</context></context-group> </trans-unit> - <trans-unit id="_msg624"> + <trans-unit id="_msg627"> <source xml:space="preserve">Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">765</context></context-group> </trans-unit> - <trans-unit id="_msg625"> + <trans-unit id="_msg628"> <source xml:space="preserve">Warning: Fee estimation is currently not possible.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">774</context></context-group> </trans-unit> - <trans-unit id="_msg626"> + <trans-unit id="_msg629"> <source xml:space="preserve">per kilobyte</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">856</context></context-group> </trans-unit> - <trans-unit id="_msg627"> + <trans-unit id="_msg630"> <source xml:space="preserve">Hide</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">803</context></context-group> </trans-unit> - <trans-unit id="_msg628"> + <trans-unit id="_msg631"> <source xml:space="preserve">Recommended:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">915</context></context-group> </trans-unit> - <trans-unit id="_msg629"> + <trans-unit id="_msg632"> <source xml:space="preserve">Custom:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">945</context></context-group> </trans-unit> - <trans-unit id="_msg630" approved="yes"> + <trans-unit id="_msg633" approved="yes"> <source xml:space="preserve">Send to multiple recipients at once</source> <target xml:space="preserve">Send to multiple recipients at once</target> <context-group purpose="location"><context context-type="linenumber">1160</context></context-group> </trans-unit> - <trans-unit id="_msg631" approved="yes"> + <trans-unit id="_msg634" approved="yes"> <source xml:space="preserve">Add &Recipient</source> <target xml:space="preserve">Add &Recipient</target> <context-group purpose="location"><context context-type="linenumber">1163</context></context-group> </trans-unit> - <trans-unit id="_msg632"> + <trans-unit id="_msg635"> <source xml:space="preserve">Clear all fields of the form.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1143</context></context-group> </trans-unit> - <trans-unit id="_msg633"> + <trans-unit id="_msg636"> <source xml:space="preserve">Inputs…</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">110</context></context-group> </trans-unit> - <trans-unit id="_msg634"> + <trans-unit id="_msg637"> <source xml:space="preserve">Dust:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">343</context></context-group> </trans-unit> - <trans-unit id="_msg635"> + <trans-unit id="_msg638"> <source xml:space="preserve">Choose…</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">741</context></context-group> </trans-unit> - <trans-unit id="_msg636"> + <trans-unit id="_msg639"> <source xml:space="preserve">Hide transaction fee settings</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">800</context></context-group> </trans-unit> - <trans-unit id="_msg637"> + <trans-unit id="_msg640"> <source xml:space="preserve">Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size. Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 satoshis per kvB" for a transaction size of 500 virtual bytes (half of 1 kvB) would ultimately yield a fee of only 50 satoshis.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">851</context></context-group> </trans-unit> - <trans-unit id="_msg638"> + <trans-unit id="_msg641"> <source xml:space="preserve">When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">886</context></context-group> </trans-unit> - <trans-unit id="_msg639"> + <trans-unit id="_msg642"> <source xml:space="preserve">A too low fee might result in a never confirming transaction (read the tooltip)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">889</context></context-group> </trans-unit> - <trans-unit id="_msg640"> + <trans-unit id="_msg643"> <source xml:space="preserve">(Smart fee not initialized yet. This usually takes a few blocks…)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">994</context></context-group> </trans-unit> - <trans-unit id="_msg641"> + <trans-unit id="_msg644"> <source xml:space="preserve">Confirmation time target:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1020</context></context-group> </trans-unit> - <trans-unit id="_msg642"> + <trans-unit id="_msg645"> <source xml:space="preserve">Enable Replace-By-Fee</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1078</context></context-group> </trans-unit> - <trans-unit id="_msg643"> + <trans-unit id="_msg646"> <source xml:space="preserve">With Replace-By-Fee (BIP-125) you can increase a transaction's fee after it is sent. Without this, a higher fee may be recommended to compensate for increased transaction delay risk.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1081</context></context-group> </trans-unit> - <trans-unit id="_msg644" approved="yes"> + <trans-unit id="_msg647" approved="yes"> <source xml:space="preserve">Clear &All</source> <target xml:space="preserve">Clear &All</target> <context-group purpose="location"><context context-type="linenumber">1146</context></context-group> </trans-unit> - <trans-unit id="_msg645" approved="yes"> + <trans-unit id="_msg648" approved="yes"> <source xml:space="preserve">Balance:</source> <target xml:space="preserve">Balance:</target> <context-group purpose="location"><context context-type="linenumber">1201</context></context-group> </trans-unit> - <trans-unit id="_msg646" approved="yes"> + <trans-unit id="_msg649" approved="yes"> <source xml:space="preserve">Confirm the send action</source> <target xml:space="preserve">Confirm the send action</target> <context-group purpose="location"><context context-type="linenumber">1117</context></context-group> </trans-unit> - <trans-unit id="_msg647" approved="yes"> + <trans-unit id="_msg650" approved="yes"> <source xml:space="preserve">S&end</source> <target xml:space="preserve">S&end</target> <context-group purpose="location"><context context-type="linenumber">1120</context></context-group> @@ -3557,344 +3581,344 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../sendcoinsdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SendCoinsDialog"> - <trans-unit id="_msg648"> + <trans-unit id="_msg651"> <source xml:space="preserve">Copy quantity</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">92</context></context-group> </trans-unit> - <trans-unit id="_msg649"> + <trans-unit id="_msg652"> <source xml:space="preserve">Copy amount</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">93</context></context-group> </trans-unit> - <trans-unit id="_msg650"> + <trans-unit id="_msg653"> <source xml:space="preserve">Copy fee</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">94</context></context-group> </trans-unit> - <trans-unit id="_msg651"> + <trans-unit id="_msg654"> <source xml:space="preserve">Copy after fee</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">95</context></context-group> </trans-unit> - <trans-unit id="_msg652"> + <trans-unit id="_msg655"> <source xml:space="preserve">Copy bytes</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">96</context></context-group> </trans-unit> - <trans-unit id="_msg653"> + <trans-unit id="_msg656"> <source xml:space="preserve">Copy dust</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">97</context></context-group> </trans-unit> - <trans-unit id="_msg654"> + <trans-unit id="_msg657"> <source xml:space="preserve">Copy change</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">98</context></context-group> </trans-unit> - <trans-unit id="_msg655"> + <trans-unit id="_msg658"> <source xml:space="preserve">%1 (%2 blocks)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">174</context></context-group> </trans-unit> - <trans-unit id="_msg656"> + <trans-unit id="_msg659"> <source xml:space="preserve">Cr&eate Unsigned</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">203</context></context-group> </trans-unit> - <trans-unit id="_msg657"> + <trans-unit id="_msg660"> <source xml:space="preserve">Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">204</context></context-group> </trans-unit> - <trans-unit id="_msg658"> + <trans-unit id="_msg661"> <source xml:space="preserve"> from wallet '%1'</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">294</context></context-group> </trans-unit> - <trans-unit id="_msg659"> + <trans-unit id="_msg662"> <source xml:space="preserve">%1 to '%2'</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">305</context></context-group> </trans-unit> - <trans-unit id="_msg660"> + <trans-unit id="_msg663"> <source xml:space="preserve">%1 to %2</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">310</context></context-group> </trans-unit> - <trans-unit id="_msg661"> + <trans-unit id="_msg664"> <source xml:space="preserve">Do you want to draft this transaction?</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">317</context></context-group> </trans-unit> - <trans-unit id="_msg662"> + <trans-unit id="_msg665"> <source xml:space="preserve">Are you sure you want to send?</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">319</context></context-group> </trans-unit> - <trans-unit id="_msg663"> + <trans-unit id="_msg666"> <source xml:space="preserve">To review recipient list click "Show Details…"</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">371</context></context-group> </trans-unit> - <trans-unit id="_msg664"> + <trans-unit id="_msg667"> <source xml:space="preserve">Create Unsigned</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">390</context></context-group> </trans-unit> - <trans-unit id="_msg665"> + <trans-unit id="_msg668"> <source xml:space="preserve">Save Transaction Data</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">434</context></context-group> </trans-unit> - <trans-unit id="_msg666"> + <trans-unit id="_msg669"> + <source xml:space="preserve">Partially Signed Transaction (Binary)</source> + <target xml:space="preserve"></target> + <context-group purpose="location"><context context-type="linenumber">436</context></context-group> + <note annotates="source" from="developer">Expanded name of the binary PSBT file format. See: BIP 174.</note> + </trans-unit> + <trans-unit id="_msg670"> <source xml:space="preserve">PSBT saved</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">442</context></context-group> + <context-group purpose="location"><context context-type="linenumber">443</context></context-group> </trans-unit> - <trans-unit id="_msg667"> + <trans-unit id="_msg671"> <source xml:space="preserve">or</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">367</context></context-group> </trans-unit> - <trans-unit id="_msg668"> + <trans-unit id="_msg672"> <source xml:space="preserve">You can increase the fee later (signals Replace-By-Fee, BIP-125).</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">348</context></context-group> </trans-unit> - <trans-unit id="_msg669"> + <trans-unit id="_msg673"> <source xml:space="preserve">Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">324</context></context-group> </trans-unit> - <trans-unit id="_msg670"> + <trans-unit id="_msg674"> <source xml:space="preserve">Please, review your transaction.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">326</context></context-group> </trans-unit> - <trans-unit id="_msg671"> + <trans-unit id="_msg675"> <source xml:space="preserve">Transaction fee</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">334</context></context-group> </trans-unit> - <trans-unit id="_msg672"> + <trans-unit id="_msg676"> <source xml:space="preserve">Not signalling Replace-By-Fee, BIP-125.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">350</context></context-group> </trans-unit> - <trans-unit id="_msg673"> + <trans-unit id="_msg677"> <source xml:space="preserve">Total Amount</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">364</context></context-group> </trans-unit> - <trans-unit id="_msg674"> + <trans-unit id="_msg678"> <source xml:space="preserve">Confirm send coins</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">389</context></context-group> </trans-unit> - <trans-unit id="_msg675"> + <trans-unit id="_msg679"> <source xml:space="preserve">Confirm transaction proposal</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">389</context></context-group> </trans-unit> - <trans-unit id="_msg676"> + <trans-unit id="_msg680"> <source xml:space="preserve">Send</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">390</context></context-group> </trans-unit> - <trans-unit id="_msg677"> - <source xml:space="preserve">Partially Signed Transaction (Binary)</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">435</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">Name of binary PSBT file format</context></context-group> - </trans-unit> - <trans-unit id="_msg678"> + <trans-unit id="_msg681"> <source xml:space="preserve">Watch-only balance:</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">618</context></context-group> + <context-group purpose="location"><context context-type="linenumber">619</context></context-group> </trans-unit> - <trans-unit id="_msg679"> + <trans-unit id="_msg682"> <source xml:space="preserve">The recipient address is not valid. Please recheck.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">642</context></context-group> + <context-group purpose="location"><context context-type="linenumber">643</context></context-group> </trans-unit> - <trans-unit id="_msg680"> + <trans-unit id="_msg683"> <source xml:space="preserve">The amount to pay must be larger than 0.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">645</context></context-group> + <context-group purpose="location"><context context-type="linenumber">646</context></context-group> </trans-unit> - <trans-unit id="_msg681"> + <trans-unit id="_msg684"> <source xml:space="preserve">The amount exceeds your balance.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">648</context></context-group> + <context-group purpose="location"><context context-type="linenumber">649</context></context-group> </trans-unit> - <trans-unit id="_msg682"> + <trans-unit id="_msg685"> <source xml:space="preserve">The total exceeds your balance when the %1 transaction fee is included.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">651</context></context-group> + <context-group purpose="location"><context context-type="linenumber">652</context></context-group> </trans-unit> - <trans-unit id="_msg683"> + <trans-unit id="_msg686"> <source xml:space="preserve">Duplicate address found: addresses should only be used once each.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">654</context></context-group> + <context-group purpose="location"><context context-type="linenumber">655</context></context-group> </trans-unit> - <trans-unit id="_msg684"> + <trans-unit id="_msg687"> <source xml:space="preserve">Transaction creation failed!</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">657</context></context-group> + <context-group purpose="location"><context context-type="linenumber">658</context></context-group> </trans-unit> - <trans-unit id="_msg685"> + <trans-unit id="_msg688"> <source xml:space="preserve">A fee higher than %1 is considered an absurdly high fee.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">661</context></context-group> + <context-group purpose="location"><context context-type="linenumber">662</context></context-group> </trans-unit> - <trans-unit id="_msg686"> + <trans-unit id="_msg689"> <source xml:space="preserve">Payment request expired.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">664</context></context-group> + <context-group purpose="location"><context context-type="linenumber">665</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">788</context></context-group> - <trans-unit id="_msg687[0]" approved="yes"> + <context-group purpose="location"><context context-type="linenumber">789</context></context-group> + <trans-unit id="_msg690[0]" approved="yes"> <source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source> <target xml:space="preserve">Estimated to begin confirmation within %n block.</target> </trans-unit> - <trans-unit id="_msg687[1]" approved="yes"> + <trans-unit id="_msg690[1]" approved="yes"> <source xml:space="preserve">Estimated to begin confirmation within %n block(s).</source> <target xml:space="preserve">Estimated to begin confirmation within %n blocks.</target> </trans-unit> </group> - <trans-unit id="_msg688"> + <trans-unit id="_msg691"> <source xml:space="preserve">Warning: Invalid Bitcoin address</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">889</context></context-group> + <context-group purpose="location"><context context-type="linenumber">890</context></context-group> </trans-unit> - <trans-unit id="_msg689"> + <trans-unit id="_msg692"> <source xml:space="preserve">Warning: Unknown change address</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">894</context></context-group> + <context-group purpose="location"><context context-type="linenumber">895</context></context-group> </trans-unit> - <trans-unit id="_msg690"> + <trans-unit id="_msg693"> <source xml:space="preserve">Confirm custom change address</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">897</context></context-group> + <context-group purpose="location"><context context-type="linenumber">898</context></context-group> </trans-unit> - <trans-unit id="_msg691"> + <trans-unit id="_msg694"> <source xml:space="preserve">The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">897</context></context-group> + <context-group purpose="location"><context context-type="linenumber">898</context></context-group> </trans-unit> - <trans-unit id="_msg692"> + <trans-unit id="_msg695"> <source xml:space="preserve">(no label)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">918</context></context-group> + <context-group purpose="location"><context context-type="linenumber">919</context></context-group> </trans-unit> </group> </body></file> <file original="../forms/sendcoinsentry.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SendCoinsEntry"> - <trans-unit id="_msg693" approved="yes"> + <trans-unit id="_msg696" approved="yes"> <source xml:space="preserve">A&mount:</source> <target xml:space="preserve">A&mount:</target> <context-group purpose="location"><context context-type="linenumber">155</context></context-group> <context-group purpose="location"><context context-type="linenumber">705</context></context-group> <context-group purpose="location"><context context-type="linenumber">1238</context></context-group> </trans-unit> - <trans-unit id="_msg694" approved="yes"> + <trans-unit id="_msg697" approved="yes"> <source xml:space="preserve">Pay &To:</source> <target xml:space="preserve">Pay &To:</target> <context-group purpose="location"><context context-type="linenumber">39</context></context-group> </trans-unit> - <trans-unit id="_msg695" approved="yes"> + <trans-unit id="_msg698" approved="yes"> <source xml:space="preserve">&Label:</source> <target xml:space="preserve">&Label:</target> <context-group purpose="location"><context context-type="linenumber">132</context></context-group> </trans-unit> - <trans-unit id="_msg696"> + <trans-unit id="_msg699"> <source xml:space="preserve">Choose previously used address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">64</context></context-group> </trans-unit> - <trans-unit id="_msg697"> + <trans-unit id="_msg700"> <source xml:space="preserve">The Bitcoin address to send the payment to</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">57</context></context-group> </trans-unit> - <trans-unit id="_msg698" approved="yes"> + <trans-unit id="_msg701" approved="yes"> <source xml:space="preserve">Alt+A</source> <target xml:space="preserve">Alt+A</target> <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> - <trans-unit id="_msg699" approved="yes"> + <trans-unit id="_msg702" approved="yes"> <source xml:space="preserve">Paste address from clipboard</source> <target xml:space="preserve">Paste address from clipboard</target> <context-group purpose="location"><context context-type="linenumber">87</context></context-group> </trans-unit> - <trans-unit id="_msg700" approved="yes"> + <trans-unit id="_msg703" approved="yes"> <source xml:space="preserve">Alt+P</source> <target xml:space="preserve">Alt+P</target> <context-group purpose="location"><context context-type="linenumber">103</context></context-group> </trans-unit> - <trans-unit id="_msg701"> + <trans-unit id="_msg704"> <source xml:space="preserve">Remove this entry</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">110</context></context-group> <context-group purpose="location"><context context-type="linenumber">672</context></context-group> <context-group purpose="location"><context context-type="linenumber">1205</context></context-group> </trans-unit> - <trans-unit id="_msg702"> + <trans-unit id="_msg705"> <source xml:space="preserve">The amount to send in the selected unit</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">170</context></context-group> </trans-unit> - <trans-unit id="_msg703"> + <trans-unit id="_msg706"> <source xml:space="preserve">The fee will be deducted from the amount being sent. The recipient will receive less bitcoins than you enter in the amount field. If multiple recipients are selected, the fee is split equally.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">177</context></context-group> </trans-unit> - <trans-unit id="_msg704"> + <trans-unit id="_msg707"> <source xml:space="preserve">S&ubtract fee from amount</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> - <trans-unit id="_msg705"> + <trans-unit id="_msg708"> <source xml:space="preserve">Use available balance</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">187</context></context-group> </trans-unit> - <trans-unit id="_msg706"> + <trans-unit id="_msg709"> <source xml:space="preserve">Message:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">196</context></context-group> </trans-unit> - <trans-unit id="_msg707"> + <trans-unit id="_msg710"> <source xml:space="preserve">This is an unauthenticated payment request.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">639</context></context-group> </trans-unit> - <trans-unit id="_msg708"> + <trans-unit id="_msg711"> <source xml:space="preserve">This is an authenticated payment request.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">1168</context></context-group> </trans-unit> - <trans-unit id="_msg709"> + <trans-unit id="_msg712"> <source xml:space="preserve">Enter a label for this address to add it to the list of used addresses</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">145</context></context-group> <context-group purpose="location"><context context-type="linenumber">148</context></context-group> </trans-unit> - <trans-unit id="_msg710"> + <trans-unit id="_msg713"> <source xml:space="preserve">A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">206</context></context-group> </trans-unit> - <trans-unit id="_msg711"> + <trans-unit id="_msg714"> <source xml:space="preserve">Pay To:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">654</context></context-group> <context-group purpose="location"><context context-type="linenumber">1183</context></context-group> </trans-unit> - <trans-unit id="_msg712"> + <trans-unit id="_msg715"> <source xml:space="preserve">Memo:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">688</context></context-group> @@ -3904,128 +3928,128 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../forms/signverifymessagedialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog"> - <trans-unit id="_msg713" approved="yes"> + <trans-unit id="_msg716" approved="yes"> <source xml:space="preserve">Signatures - Sign / Verify a Message</source> <target xml:space="preserve">Signatures - Sign / Verify a Message</target> <context-group purpose="location"><context context-type="linenumber">14</context></context-group> </trans-unit> - <trans-unit id="_msg714" approved="yes"> + <trans-unit id="_msg717" approved="yes"> <source xml:space="preserve">&Sign Message</source> <target xml:space="preserve">&Sign Message</target> <context-group purpose="location"><context context-type="linenumber">27</context></context-group> </trans-unit> - <trans-unit id="_msg715"> + <trans-unit id="_msg718"> <source xml:space="preserve">You can sign messages/agreements with your addresses to prove you can receive bitcoins sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">33</context></context-group> </trans-unit> - <trans-unit id="_msg716"> + <trans-unit id="_msg719"> <source xml:space="preserve">The Bitcoin address to sign the message with</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">51</context></context-group> </trans-unit> - <trans-unit id="_msg717"> + <trans-unit id="_msg720"> <source xml:space="preserve">Choose previously used address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> <context-group purpose="location"><context context-type="linenumber">274</context></context-group> </trans-unit> - <trans-unit id="_msg718" approved="yes"> + <trans-unit id="_msg721" approved="yes"> <source xml:space="preserve">Alt+A</source> <target xml:space="preserve">Alt+A</target> <context-group purpose="location"><context context-type="linenumber">68</context></context-group> <context-group purpose="location"><context context-type="linenumber">284</context></context-group> </trans-unit> - <trans-unit id="_msg719" approved="yes"> + <trans-unit id="_msg722" approved="yes"> <source xml:space="preserve">Paste address from clipboard</source> <target xml:space="preserve">Paste address from clipboard</target> <context-group purpose="location"><context context-type="linenumber">78</context></context-group> </trans-unit> - <trans-unit id="_msg720" approved="yes"> + <trans-unit id="_msg723" approved="yes"> <source xml:space="preserve">Alt+P</source> <target xml:space="preserve">Alt+P</target> <context-group purpose="location"><context context-type="linenumber">88</context></context-group> </trans-unit> - <trans-unit id="_msg721" approved="yes"> + <trans-unit id="_msg724" approved="yes"> <source xml:space="preserve">Enter the message you want to sign here</source> <target xml:space="preserve">Enter the message you want to sign here</target> <context-group purpose="location"><context context-type="linenumber">100</context></context-group> <context-group purpose="location"><context context-type="linenumber">103</context></context-group> </trans-unit> - <trans-unit id="_msg722" approved="yes"> + <trans-unit id="_msg725" approved="yes"> <source xml:space="preserve">Signature</source> <target xml:space="preserve">Signature</target> <context-group purpose="location"><context context-type="linenumber">110</context></context-group> </trans-unit> - <trans-unit id="_msg723" approved="yes"> + <trans-unit id="_msg726" approved="yes"> <source xml:space="preserve">Copy the current signature to the system clipboard</source> <target xml:space="preserve">Copy the current signature to the system clipboard</target> <context-group purpose="location"><context context-type="linenumber">140</context></context-group> </trans-unit> - <trans-unit id="_msg724" approved="yes"> + <trans-unit id="_msg727" approved="yes"> <source xml:space="preserve">Sign the message to prove you own this Bitcoin address</source> <target xml:space="preserve">Sign the message to prove you own this Bitcoin address</target> <context-group purpose="location"><context context-type="linenumber">161</context></context-group> </trans-unit> - <trans-unit id="_msg725" approved="yes"> + <trans-unit id="_msg728" approved="yes"> <source xml:space="preserve">Sign &Message</source> <target xml:space="preserve">Sign &Message</target> <context-group purpose="location"><context context-type="linenumber">164</context></context-group> </trans-unit> - <trans-unit id="_msg726" approved="yes"> + <trans-unit id="_msg729" approved="yes"> <source xml:space="preserve">Reset all sign message fields</source> <target xml:space="preserve">Reset all sign message fields</target> <context-group purpose="location"><context context-type="linenumber">178</context></context-group> </trans-unit> - <trans-unit id="_msg727" approved="yes"> + <trans-unit id="_msg730" approved="yes"> <source xml:space="preserve">Clear &All</source> <target xml:space="preserve">Clear &All</target> <context-group purpose="location"><context context-type="linenumber">181</context></context-group> <context-group purpose="location"><context context-type="linenumber">338</context></context-group> </trans-unit> - <trans-unit id="_msg728" approved="yes"> + <trans-unit id="_msg731" approved="yes"> <source xml:space="preserve">&Verify Message</source> <target xml:space="preserve">&Verify Message</target> <context-group purpose="location"><context context-type="linenumber">240</context></context-group> </trans-unit> - <trans-unit id="_msg729"> + <trans-unit id="_msg732"> <source xml:space="preserve">Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">246</context></context-group> </trans-unit> - <trans-unit id="_msg730"> + <trans-unit id="_msg733"> <source xml:space="preserve">The Bitcoin address the message was signed with</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">267</context></context-group> </trans-unit> - <trans-unit id="_msg731"> + <trans-unit id="_msg734"> <source xml:space="preserve">The signed message to verify</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">296</context></context-group> <context-group purpose="location"><context context-type="linenumber">299</context></context-group> </trans-unit> - <trans-unit id="_msg732"> + <trans-unit id="_msg735"> <source xml:space="preserve">The signature given when the message was signed</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">306</context></context-group> <context-group purpose="location"><context context-type="linenumber">309</context></context-group> </trans-unit> - <trans-unit id="_msg733" approved="yes"> + <trans-unit id="_msg736" approved="yes"> <source xml:space="preserve">Verify the message to ensure it was signed with the specified Bitcoin address</source> <target xml:space="preserve">Verify the message to ensure it was signed with the specified Bitcoin address</target> <context-group purpose="location"><context context-type="linenumber">318</context></context-group> </trans-unit> - <trans-unit id="_msg734" approved="yes"> + <trans-unit id="_msg737" approved="yes"> <source xml:space="preserve">Verify &Message</source> <target xml:space="preserve">Verify &Message</target> <context-group purpose="location"><context context-type="linenumber">321</context></context-group> </trans-unit> - <trans-unit id="_msg735" approved="yes"> + <trans-unit id="_msg738" approved="yes"> <source xml:space="preserve">Reset all verify message fields</source> <target xml:space="preserve">Reset all verify message fields</target> <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg736"> + <trans-unit id="_msg739"> <source xml:space="preserve">Click "Sign Message" to generate signature</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">125</context></context-group> @@ -4034,13 +4058,13 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../signverifymessagedialog.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="SignVerifyMessageDialog"> - <trans-unit id="_msg737"> + <trans-unit id="_msg740"> <source xml:space="preserve">The entered address is invalid.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> <context-group purpose="location"><context context-type="linenumber">219</context></context-group> </trans-unit> - <trans-unit id="_msg738"> + <trans-unit id="_msg741"> <source xml:space="preserve">Please check the address and try again.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">120</context></context-group> @@ -4048,59 +4072,59 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <context-group purpose="location"><context context-type="linenumber">220</context></context-group> <context-group purpose="location"><context context-type="linenumber">227</context></context-group> </trans-unit> - <trans-unit id="_msg739"> + <trans-unit id="_msg742"> <source xml:space="preserve">The entered address does not refer to a key.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">127</context></context-group> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg740"> + <trans-unit id="_msg743"> <source xml:space="preserve">Wallet unlock was cancelled.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">135</context></context-group> </trans-unit> - <trans-unit id="_msg741"> + <trans-unit id="_msg744"> <source xml:space="preserve">No error</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> - <trans-unit id="_msg742"> + <trans-unit id="_msg745"> <source xml:space="preserve">Private key for the entered address is not available.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">149</context></context-group> </trans-unit> - <trans-unit id="_msg743"> + <trans-unit id="_msg746"> <source xml:space="preserve">Message signing failed.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">152</context></context-group> </trans-unit> - <trans-unit id="_msg744"> + <trans-unit id="_msg747"> <source xml:space="preserve">Message signed.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">164</context></context-group> </trans-unit> - <trans-unit id="_msg745"> + <trans-unit id="_msg748"> <source xml:space="preserve">The signature could not be decoded.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">233</context></context-group> </trans-unit> - <trans-unit id="_msg746"> + <trans-unit id="_msg749"> <source xml:space="preserve">Please check the signature and try again.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">234</context></context-group> <context-group purpose="location"><context context-type="linenumber">241</context></context-group> </trans-unit> - <trans-unit id="_msg747"> + <trans-unit id="_msg750"> <source xml:space="preserve">The signature did not match the message digest.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">240</context></context-group> </trans-unit> - <trans-unit id="_msg748"> + <trans-unit id="_msg751"> <source xml:space="preserve">Message verification failed.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">246</context></context-group> </trans-unit> - <trans-unit id="_msg749"> + <trans-unit id="_msg752"> <source xml:space="preserve">Message verified.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">214</context></context-group> @@ -4109,7 +4133,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../trafficgraphwidget.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TrafficGraphWidget"> - <trans-unit id="_msg750"> + <trans-unit id="_msg753"> <source xml:space="preserve">kB/s</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">82</context></context-group> @@ -4119,246 +4143,246 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 <file original="../transactiondesc.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionDesc"> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">34</context></context-group> - <trans-unit id="_msg751[0]" approved="yes"> + <context-group purpose="location"><context context-type="linenumber">36</context></context-group> + <trans-unit id="_msg754[0]" approved="yes"> <source xml:space="preserve">Open for %n more block(s)</source> <target xml:space="preserve">Open for %n more block</target> </trans-unit> - <trans-unit id="_msg751[1]" approved="yes"> + <trans-unit id="_msg754[1]" approved="yes"> <source xml:space="preserve">Open for %n more block(s)</source> <target xml:space="preserve">Open for %n more blocks</target> </trans-unit> </group> - <trans-unit id="_msg752"> + <trans-unit id="_msg755"> <source xml:space="preserve">Open until %1</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">36</context></context-group> + <context-group purpose="location"><context context-type="linenumber">38</context></context-group> </trans-unit> - <trans-unit id="_msg753"> + <trans-unit id="_msg756"> <source xml:space="preserve">conflicted with a transaction with %1 confirmations</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">42</context></context-group> + <context-group purpose="location"><context context-type="linenumber">44</context></context-group> </trans-unit> - <trans-unit id="_msg754"> + <trans-unit id="_msg757"> <source xml:space="preserve">0/unconfirmed, %1</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">44</context></context-group> + <context-group purpose="location"><context context-type="linenumber">47</context></context-group> </trans-unit> - <trans-unit id="_msg755"> + <trans-unit id="_msg758"> <source xml:space="preserve">in memory pool</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">44</context></context-group> + <context-group purpose="location"><context context-type="linenumber">47</context></context-group> </trans-unit> - <trans-unit id="_msg756"> + <trans-unit id="_msg759"> <source xml:space="preserve">not in memory pool</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">44</context></context-group> + <context-group purpose="location"><context context-type="linenumber">47</context></context-group> </trans-unit> - <trans-unit id="_msg757"> + <trans-unit id="_msg760"> <source xml:space="preserve">abandoned</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">44</context></context-group> + <context-group purpose="location"><context context-type="linenumber">46</context></context-group> </trans-unit> - <trans-unit id="_msg758"> + <trans-unit id="_msg761"> <source xml:space="preserve">%1/unconfirmed</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">46</context></context-group> + <context-group purpose="location"><context context-type="linenumber">49</context></context-group> </trans-unit> - <trans-unit id="_msg759"> + <trans-unit id="_msg762"> <source xml:space="preserve">%1 confirmations</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">48</context></context-group> + <context-group purpose="location"><context context-type="linenumber">51</context></context-group> </trans-unit> - <trans-unit id="_msg760"> + <trans-unit id="_msg763"> <source xml:space="preserve">Status</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">98</context></context-group> + <context-group purpose="location"><context context-type="linenumber">102</context></context-group> </trans-unit> - <trans-unit id="_msg761"> + <trans-unit id="_msg764"> <source xml:space="preserve">Date</source> <target xml:space="preserve" state="needs-review-translation">Date</target> - <context-group purpose="location"><context context-type="linenumber">101</context></context-group> + <context-group purpose="location"><context context-type="linenumber">105</context></context-group> </trans-unit> - <trans-unit id="_msg762"> + <trans-unit id="_msg765"> <source xml:space="preserve">Source</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">108</context></context-group> + <context-group purpose="location"><context context-type="linenumber">112</context></context-group> </trans-unit> - <trans-unit id="_msg763"> + <trans-unit id="_msg766"> <source xml:space="preserve">Generated</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">108</context></context-group> + <context-group purpose="location"><context context-type="linenumber">112</context></context-group> </trans-unit> - <trans-unit id="_msg764"> + <trans-unit id="_msg767"> <source xml:space="preserve">From</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">113</context></context-group> - <context-group purpose="location"><context context-type="linenumber">127</context></context-group> - <context-group purpose="location"><context context-type="linenumber">199</context></context-group> + <context-group purpose="location"><context context-type="linenumber">117</context></context-group> + <context-group purpose="location"><context context-type="linenumber">131</context></context-group> + <context-group purpose="location"><context context-type="linenumber">203</context></context-group> </trans-unit> - <trans-unit id="_msg765"> + <trans-unit id="_msg768"> <source xml:space="preserve">unknown</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">127</context></context-group> + <context-group purpose="location"><context context-type="linenumber">131</context></context-group> </trans-unit> - <trans-unit id="_msg766"> + <trans-unit id="_msg769"> <source xml:space="preserve">To</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">128</context></context-group> - <context-group purpose="location"><context context-type="linenumber">148</context></context-group> - <context-group purpose="location"><context context-type="linenumber">218</context></context-group> + <context-group purpose="location"><context context-type="linenumber">132</context></context-group> + <context-group purpose="location"><context context-type="linenumber">152</context></context-group> + <context-group purpose="location"><context context-type="linenumber">222</context></context-group> </trans-unit> - <trans-unit id="_msg767"> + <trans-unit id="_msg770"> <source xml:space="preserve">own address</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">130</context></context-group> + <context-group purpose="location"><context context-type="linenumber">134</context></context-group> </trans-unit> - <trans-unit id="_msg768"> + <trans-unit id="_msg771"> <source xml:space="preserve">watch-only</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">130</context></context-group> - <context-group purpose="location"><context context-type="linenumber">199</context></context-group> + <context-group purpose="location"><context context-type="linenumber">134</context></context-group> + <context-group purpose="location"><context context-type="linenumber">203</context></context-group> </trans-unit> - <trans-unit id="_msg769"> + <trans-unit id="_msg772"> <source xml:space="preserve">label</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">132</context></context-group> + <context-group purpose="location"><context context-type="linenumber">136</context></context-group> </trans-unit> - <trans-unit id="_msg770"> + <trans-unit id="_msg773"> <source xml:space="preserve">Credit</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">168</context></context-group> - <context-group purpose="location"><context context-type="linenumber">180</context></context-group> - <context-group purpose="location"><context context-type="linenumber">234</context></context-group> - <context-group purpose="location"><context context-type="linenumber">264</context></context-group> - <context-group purpose="location"><context context-type="linenumber">324</context></context-group> + <context-group purpose="location"><context context-type="linenumber">172</context></context-group> + <context-group purpose="location"><context context-type="linenumber">184</context></context-group> + <context-group purpose="location"><context context-type="linenumber">238</context></context-group> + <context-group purpose="location"><context context-type="linenumber">268</context></context-group> + <context-group purpose="location"><context context-type="linenumber">328</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">170</context></context-group> - <trans-unit id="_msg771[0]" approved="yes"> + <context-group purpose="location"><context context-type="linenumber">174</context></context-group> + <trans-unit id="_msg774[0]" approved="yes"> <source xml:space="preserve">matures in %n more block(s)</source> <target xml:space="preserve">matures in %n more block</target> </trans-unit> - <trans-unit id="_msg771[1]" approved="yes"> + <trans-unit id="_msg774[1]" approved="yes"> <source xml:space="preserve">matures in %n more block(s)</source> <target xml:space="preserve">matures in %n more blocks</target> </trans-unit> </group> - <trans-unit id="_msg772"> + <trans-unit id="_msg775"> <source xml:space="preserve">not accepted</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">172</context></context-group> + <context-group purpose="location"><context context-type="linenumber">176</context></context-group> </trans-unit> - <trans-unit id="_msg773"> + <trans-unit id="_msg776"> <source xml:space="preserve">Debit</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">232</context></context-group> - <context-group purpose="location"><context context-type="linenumber">258</context></context-group> - <context-group purpose="location"><context context-type="linenumber">321</context></context-group> + <context-group purpose="location"><context context-type="linenumber">236</context></context-group> + <context-group purpose="location"><context context-type="linenumber">262</context></context-group> + <context-group purpose="location"><context context-type="linenumber">325</context></context-group> </trans-unit> - <trans-unit id="_msg774"> + <trans-unit id="_msg777"> <source xml:space="preserve">Total debit</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">242</context></context-group> + <context-group purpose="location"><context context-type="linenumber">246</context></context-group> </trans-unit> - <trans-unit id="_msg775"> + <trans-unit id="_msg778"> <source xml:space="preserve">Total credit</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">243</context></context-group> + <context-group purpose="location"><context context-type="linenumber">247</context></context-group> </trans-unit> - <trans-unit id="_msg776"> + <trans-unit id="_msg779"> <source xml:space="preserve">Transaction fee</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">248</context></context-group> + <context-group purpose="location"><context context-type="linenumber">252</context></context-group> </trans-unit> - <trans-unit id="_msg777"> + <trans-unit id="_msg780"> <source xml:space="preserve">Net amount</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">270</context></context-group> + <context-group purpose="location"><context context-type="linenumber">274</context></context-group> </trans-unit> - <trans-unit id="_msg778"> + <trans-unit id="_msg781"> <source xml:space="preserve">Message</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">276</context></context-group> - <context-group purpose="location"><context context-type="linenumber">288</context></context-group> + <context-group purpose="location"><context context-type="linenumber">280</context></context-group> + <context-group purpose="location"><context context-type="linenumber">292</context></context-group> </trans-unit> - <trans-unit id="_msg779"> + <trans-unit id="_msg782"> <source xml:space="preserve">Comment</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">278</context></context-group> + <context-group purpose="location"><context context-type="linenumber">282</context></context-group> </trans-unit> - <trans-unit id="_msg780"> + <trans-unit id="_msg783"> <source xml:space="preserve">Transaction ID</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">280</context></context-group> + <context-group purpose="location"><context context-type="linenumber">284</context></context-group> </trans-unit> - <trans-unit id="_msg781"> + <trans-unit id="_msg784"> <source xml:space="preserve">Transaction total size</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">281</context></context-group> + <context-group purpose="location"><context context-type="linenumber">285</context></context-group> </trans-unit> - <trans-unit id="_msg782"> + <trans-unit id="_msg785"> <source xml:space="preserve">Transaction virtual size</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">282</context></context-group> + <context-group purpose="location"><context context-type="linenumber">286</context></context-group> </trans-unit> - <trans-unit id="_msg783"> + <trans-unit id="_msg786"> <source xml:space="preserve">Output index</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">283</context></context-group> + <context-group purpose="location"><context context-type="linenumber">287</context></context-group> </trans-unit> - <trans-unit id="_msg784"> + <trans-unit id="_msg787"> <source xml:space="preserve"> (Certificate was not verified)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">299</context></context-group> + <context-group purpose="location"><context context-type="linenumber">303</context></context-group> </trans-unit> - <trans-unit id="_msg785"> + <trans-unit id="_msg788"> <source xml:space="preserve">Merchant</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">302</context></context-group> + <context-group purpose="location"><context context-type="linenumber">306</context></context-group> </trans-unit> - <trans-unit id="_msg786"> + <trans-unit id="_msg789"> <source xml:space="preserve">Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">310</context></context-group> + <context-group purpose="location"><context context-type="linenumber">314</context></context-group> </trans-unit> - <trans-unit id="_msg787"> + <trans-unit id="_msg790"> <source xml:space="preserve">Debug information</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">318</context></context-group> + <context-group purpose="location"><context context-type="linenumber">322</context></context-group> </trans-unit> - <trans-unit id="_msg788"> + <trans-unit id="_msg791"> <source xml:space="preserve">Transaction</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">326</context></context-group> + <context-group purpose="location"><context context-type="linenumber">330</context></context-group> </trans-unit> - <trans-unit id="_msg789"> + <trans-unit id="_msg792"> <source xml:space="preserve">Inputs</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">329</context></context-group> + <context-group purpose="location"><context context-type="linenumber">333</context></context-group> </trans-unit> - <trans-unit id="_msg790"> + <trans-unit id="_msg793"> <source xml:space="preserve">Amount</source> <target xml:space="preserve" state="needs-review-translation">Amount</target> - <context-group purpose="location"><context context-type="linenumber">350</context></context-group> + <context-group purpose="location"><context context-type="linenumber">354</context></context-group> </trans-unit> - <trans-unit id="_msg791"> + <trans-unit id="_msg794"> <source xml:space="preserve">true</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">351</context></context-group> - <context-group purpose="location"><context context-type="linenumber">352</context></context-group> + <context-group purpose="location"><context context-type="linenumber">355</context></context-group> + <context-group purpose="location"><context context-type="linenumber">356</context></context-group> </trans-unit> - <trans-unit id="_msg792"> + <trans-unit id="_msg795"> <source xml:space="preserve">false</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">351</context></context-group> - <context-group purpose="location"><context context-type="linenumber">352</context></context-group> + <context-group purpose="location"><context context-type="linenumber">355</context></context-group> + <context-group purpose="location"><context context-type="linenumber">356</context></context-group> </trans-unit> </group> </body></file> <file original="../forms/transactiondescdialog.ui" datatype="x-trolltech-designer-ui" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionDescDialog"> - <trans-unit id="_msg793" approved="yes"> + <trans-unit id="_msg796" approved="yes"> <source xml:space="preserve">This pane shows a detailed description of the transaction</source> <target xml:space="preserve">This pane shows a detailed description of the transaction</target> <context-group purpose="location"><context context-type="linenumber">20</context></context-group> @@ -4367,7 +4391,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../transactiondescdialog.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionDescDialog"> - <trans-unit id="_msg794"> + <trans-unit id="_msg797"> <source xml:space="preserve">Details for %1</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">18</context></context-group> @@ -4376,355 +4400,355 @@ Note: Since the fee is calculated on a per-byte basis, a fee rate of "100 </body></file> <file original="../transactiontablemodel.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionTableModel"> - <trans-unit id="_msg795"> + <trans-unit id="_msg798"> <source xml:space="preserve">Date</source> <target xml:space="preserve" state="needs-review-translation">Date</target> - <context-group purpose="location"><context context-type="linenumber">252</context></context-group> + <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> - <trans-unit id="_msg796"> + <trans-unit id="_msg799"> <source xml:space="preserve">Type</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">252</context></context-group> + <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> - <trans-unit id="_msg797"> + <trans-unit id="_msg800"> <source xml:space="preserve">Label</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">252</context></context-group> + <context-group purpose="location"><context context-type="linenumber">260</context></context-group> </trans-unit> <group restype="x-gettext-plurals"> - <context-group purpose="location"><context context-type="linenumber">314</context></context-group> - <trans-unit id="_msg798[0]" approved="yes"> + <context-group purpose="location"><context context-type="linenumber">320</context></context-group> + <trans-unit id="_msg801[0]" approved="yes"> <source xml:space="preserve">Open for %n more block(s)</source> <target xml:space="preserve">Open for %n more block</target> </trans-unit> - <trans-unit id="_msg798[1]" approved="yes"> + <trans-unit id="_msg801[1]" approved="yes"> <source xml:space="preserve">Open for %n more block(s)</source> <target xml:space="preserve">Open for %n more blocks</target> </trans-unit> </group> - <trans-unit id="_msg799"> + <trans-unit id="_msg802"> <source xml:space="preserve">Open until %1</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">317</context></context-group> + <context-group purpose="location"><context context-type="linenumber">323</context></context-group> </trans-unit> - <trans-unit id="_msg800"> + <trans-unit id="_msg803"> <source xml:space="preserve">Unconfirmed</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">320</context></context-group> + <context-group purpose="location"><context context-type="linenumber">326</context></context-group> </trans-unit> - <trans-unit id="_msg801"> + <trans-unit id="_msg804"> <source xml:space="preserve">Abandoned</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">323</context></context-group> + <context-group purpose="location"><context context-type="linenumber">329</context></context-group> </trans-unit> - <trans-unit id="_msg802"> + <trans-unit id="_msg805"> <source xml:space="preserve">Confirming (%1 of %2 recommended confirmations)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">326</context></context-group> + <context-group purpose="location"><context context-type="linenumber">332</context></context-group> </trans-unit> - <trans-unit id="_msg803"> + <trans-unit id="_msg806"> <source xml:space="preserve">Confirmed (%1 confirmations)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">329</context></context-group> + <context-group purpose="location"><context context-type="linenumber">335</context></context-group> </trans-unit> - <trans-unit id="_msg804"> + <trans-unit id="_msg807"> <source xml:space="preserve">Conflicted</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">332</context></context-group> + <context-group purpose="location"><context context-type="linenumber">338</context></context-group> </trans-unit> - <trans-unit id="_msg805"> + <trans-unit id="_msg808"> <source xml:space="preserve">Immature (%1 confirmations, will be available after %2)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">335</context></context-group> + <context-group purpose="location"><context context-type="linenumber">341</context></context-group> </trans-unit> - <trans-unit id="_msg806"> + <trans-unit id="_msg809"> <source xml:space="preserve">Generated but not accepted</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">338</context></context-group> + <context-group purpose="location"><context context-type="linenumber">344</context></context-group> </trans-unit> - <trans-unit id="_msg807"> + <trans-unit id="_msg810"> <source xml:space="preserve">Received with</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">377</context></context-group> + <context-group purpose="location"><context context-type="linenumber">383</context></context-group> </trans-unit> - <trans-unit id="_msg808"> + <trans-unit id="_msg811"> <source xml:space="preserve">Received from</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">379</context></context-group> + <context-group purpose="location"><context context-type="linenumber">385</context></context-group> </trans-unit> - <trans-unit id="_msg809"> + <trans-unit id="_msg812"> <source xml:space="preserve">Sent to</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">382</context></context-group> + <context-group purpose="location"><context context-type="linenumber">388</context></context-group> </trans-unit> - <trans-unit id="_msg810"> + <trans-unit id="_msg813"> <source xml:space="preserve">Payment to yourself</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">384</context></context-group> + <context-group purpose="location"><context context-type="linenumber">390</context></context-group> </trans-unit> - <trans-unit id="_msg811"> + <trans-unit id="_msg814"> <source xml:space="preserve">Mined</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">386</context></context-group> + <context-group purpose="location"><context context-type="linenumber">392</context></context-group> </trans-unit> - <trans-unit id="_msg812"> + <trans-unit id="_msg815"> <source xml:space="preserve">watch-only</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">414</context></context-group> + <context-group purpose="location"><context context-type="linenumber">420</context></context-group> </trans-unit> - <trans-unit id="_msg813"> + <trans-unit id="_msg816"> <source xml:space="preserve">(n/a)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">430</context></context-group> + <context-group purpose="location"><context context-type="linenumber">436</context></context-group> </trans-unit> - <trans-unit id="_msg814"> + <trans-unit id="_msg817"> <source xml:space="preserve">(no label)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">640</context></context-group> + <context-group purpose="location"><context context-type="linenumber">646</context></context-group> </trans-unit> - <trans-unit id="_msg815"> + <trans-unit id="_msg818"> <source xml:space="preserve">Transaction status. Hover over this field to show number of confirmations.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">679</context></context-group> + <context-group purpose="location"><context context-type="linenumber">685</context></context-group> </trans-unit> - <trans-unit id="_msg816"> + <trans-unit id="_msg819"> <source xml:space="preserve">Date and time that the transaction was received.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">681</context></context-group> + <context-group purpose="location"><context context-type="linenumber">687</context></context-group> </trans-unit> - <trans-unit id="_msg817"> + <trans-unit id="_msg820"> <source xml:space="preserve">Type of transaction.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">683</context></context-group> + <context-group purpose="location"><context context-type="linenumber">689</context></context-group> </trans-unit> - <trans-unit id="_msg818"> + <trans-unit id="_msg821"> <source xml:space="preserve">Whether or not a watch-only address is involved in this transaction.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">685</context></context-group> + <context-group purpose="location"><context context-type="linenumber">691</context></context-group> </trans-unit> - <trans-unit id="_msg819"> + <trans-unit id="_msg822"> <source xml:space="preserve">User-defined intent/purpose of the transaction.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">687</context></context-group> + <context-group purpose="location"><context context-type="linenumber">693</context></context-group> </trans-unit> - <trans-unit id="_msg820"> + <trans-unit id="_msg823"> <source xml:space="preserve">Amount removed from or added to balance.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">689</context></context-group> + <context-group purpose="location"><context context-type="linenumber">695</context></context-group> </trans-unit> </group> </body></file> <file original="../transactionview.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="TransactionView"> - <trans-unit id="_msg821"> + <trans-unit id="_msg824"> <source xml:space="preserve">All</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">70</context></context-group> <context-group purpose="location"><context context-type="linenumber">86</context></context-group> </trans-unit> - <trans-unit id="_msg822"> + <trans-unit id="_msg825"> <source xml:space="preserve">Today</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">71</context></context-group> </trans-unit> - <trans-unit id="_msg823"> + <trans-unit id="_msg826"> <source xml:space="preserve">This week</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">72</context></context-group> </trans-unit> - <trans-unit id="_msg824"> + <trans-unit id="_msg827"> <source xml:space="preserve">This month</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">73</context></context-group> </trans-unit> - <trans-unit id="_msg825"> + <trans-unit id="_msg828"> <source xml:space="preserve">Last month</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">74</context></context-group> </trans-unit> - <trans-unit id="_msg826"> + <trans-unit id="_msg829"> <source xml:space="preserve">This year</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">75</context></context-group> </trans-unit> - <trans-unit id="_msg827"> + <trans-unit id="_msg830"> <source xml:space="preserve">Received with</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">87</context></context-group> </trans-unit> - <trans-unit id="_msg828"> + <trans-unit id="_msg831"> <source xml:space="preserve">Sent to</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">89</context></context-group> </trans-unit> - <trans-unit id="_msg829"> + <trans-unit id="_msg832"> <source xml:space="preserve">To yourself</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">91</context></context-group> </trans-unit> - <trans-unit id="_msg830"> + <trans-unit id="_msg833"> <source xml:space="preserve">Mined</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">92</context></context-group> </trans-unit> - <trans-unit id="_msg831"> + <trans-unit id="_msg834"> <source xml:space="preserve">Other</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">93</context></context-group> </trans-unit> - <trans-unit id="_msg832"> + <trans-unit id="_msg835"> <source xml:space="preserve">Enter address, transaction id, or label to search</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">98</context></context-group> </trans-unit> - <trans-unit id="_msg833"> + <trans-unit id="_msg836"> <source xml:space="preserve">Min amount</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">102</context></context-group> </trans-unit> - <trans-unit id="_msg834"> + <trans-unit id="_msg837"> <source xml:space="preserve">Abandon transaction</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">177</context></context-group> </trans-unit> - <trans-unit id="_msg835"> + <trans-unit id="_msg838"> <source xml:space="preserve">Increase transaction fee</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">174</context></context-group> </trans-unit> - <trans-unit id="_msg836"> + <trans-unit id="_msg839"> <source xml:space="preserve">Copy address</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">166</context></context-group> </trans-unit> - <trans-unit id="_msg837"> + <trans-unit id="_msg840"> <source xml:space="preserve">Copy label</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">167</context></context-group> </trans-unit> - <trans-unit id="_msg838"> + <trans-unit id="_msg841"> <source xml:space="preserve">Copy amount</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">168</context></context-group> </trans-unit> - <trans-unit id="_msg839"> + <trans-unit id="_msg842"> <source xml:space="preserve">Copy transaction ID</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">169</context></context-group> </trans-unit> - <trans-unit id="_msg840"> + <trans-unit id="_msg843"> <source xml:space="preserve">Copy raw transaction</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">170</context></context-group> </trans-unit> - <trans-unit id="_msg841"> + <trans-unit id="_msg844"> <source xml:space="preserve">Copy full transaction details</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">171</context></context-group> </trans-unit> - <trans-unit id="_msg842"> + <trans-unit id="_msg845"> <source xml:space="preserve">Edit address label</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">178</context></context-group> </trans-unit> - <trans-unit id="_msg843"> - <source xml:space="preserve">Comma separated file</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">339</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">Name of CSV file format</context></context-group> - </trans-unit> - <trans-unit id="_msg844"> + <trans-unit id="_msg846"> <source xml:space="preserve">Show transaction details</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">172</context></context-group> </trans-unit> - <trans-unit id="_msg845"> + <trans-unit id="_msg847"> <source xml:space="preserve">Range…</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">76</context></context-group> </trans-unit> - <trans-unit id="_msg846"> + <trans-unit id="_msg848"> <source xml:space="preserve">Export Transaction History</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">338</context></context-group> </trans-unit> - <trans-unit id="_msg847"> + <trans-unit id="_msg849"> + <source xml:space="preserve">Comma separated file</source> + <target xml:space="preserve"></target> + <context-group purpose="location"><context context-type="linenumber">341</context></context-group> + <note annotates="source" from="developer">Expanded name of the CSV file format. See https://en.wikipedia.org/wiki/Comma-separated_values</note> + </trans-unit> + <trans-unit id="_msg850"> <source xml:space="preserve">Confirmed</source> <target xml:space="preserve" state="needs-review-translation">Confirmed</target> - <context-group purpose="location"><context context-type="linenumber">348</context></context-group> + <context-group purpose="location"><context context-type="linenumber">350</context></context-group> </trans-unit> - <trans-unit id="_msg848"> + <trans-unit id="_msg851"> <source xml:space="preserve">Watch-only</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">350</context></context-group> + <context-group purpose="location"><context context-type="linenumber">352</context></context-group> </trans-unit> - <trans-unit id="_msg849"> + <trans-unit id="_msg852"> <source xml:space="preserve">Date</source> <target xml:space="preserve" state="needs-review-translation">Date</target> - <context-group purpose="location"><context context-type="linenumber">351</context></context-group> + <context-group purpose="location"><context context-type="linenumber">353</context></context-group> </trans-unit> - <trans-unit id="_msg850"> + <trans-unit id="_msg853"> <source xml:space="preserve">Type</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">352</context></context-group> + <context-group purpose="location"><context context-type="linenumber">354</context></context-group> </trans-unit> - <trans-unit id="_msg851"> + <trans-unit id="_msg854"> <source xml:space="preserve">Label</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">353</context></context-group> + <context-group purpose="location"><context context-type="linenumber">355</context></context-group> </trans-unit> - <trans-unit id="_msg852"> + <trans-unit id="_msg855"> <source xml:space="preserve">Address</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">354</context></context-group> + <context-group purpose="location"><context context-type="linenumber">356</context></context-group> </trans-unit> - <trans-unit id="_msg853"> + <trans-unit id="_msg856"> <source xml:space="preserve">ID</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">356</context></context-group> + <context-group purpose="location"><context context-type="linenumber">358</context></context-group> </trans-unit> - <trans-unit id="_msg854"> + <trans-unit id="_msg857"> <source xml:space="preserve">Exporting Failed</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">359</context></context-group> + <context-group purpose="location"><context context-type="linenumber">361</context></context-group> </trans-unit> - <trans-unit id="_msg855"> + <trans-unit id="_msg858"> <source xml:space="preserve">There was an error trying to save the transaction history to %1.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">359</context></context-group> + <context-group purpose="location"><context context-type="linenumber">361</context></context-group> </trans-unit> - <trans-unit id="_msg856"> + <trans-unit id="_msg859"> <source xml:space="preserve">Exporting Successful</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">363</context></context-group> + <context-group purpose="location"><context context-type="linenumber">365</context></context-group> </trans-unit> - <trans-unit id="_msg857"> + <trans-unit id="_msg860"> <source xml:space="preserve">The transaction history was successfully saved to %1.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">363</context></context-group> + <context-group purpose="location"><context context-type="linenumber">365</context></context-group> </trans-unit> - <trans-unit id="_msg858"> + <trans-unit id="_msg861"> <source xml:space="preserve">Range:</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">535</context></context-group> + <context-group purpose="location"><context context-type="linenumber">537</context></context-group> </trans-unit> - <trans-unit id="_msg859"> + <trans-unit id="_msg862"> <source xml:space="preserve">to</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">543</context></context-group> + <context-group purpose="location"><context context-type="linenumber">545</context></context-group> </trans-unit> </group> </body></file> <file original="../walletframe.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="WalletFrame"> - <trans-unit id="_msg860"> + <trans-unit id="_msg863"> <source xml:space="preserve">No wallet has been loaded. Go to File > Open Wallet to load a wallet. - OR -</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">39</context></context-group> </trans-unit> - <trans-unit id="_msg861"> + <trans-unit id="_msg864"> <source xml:space="preserve">Create a new wallet</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">44</context></context-group> @@ -4733,12 +4757,12 @@ Go to File > Open Wallet to load a wallet. </body></file> <file original="../walletmodel.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="WalletModel"> - <trans-unit id="_msg862"> + <trans-unit id="_msg865"> <source xml:space="preserve">Send Coins</source> <target xml:space="preserve" state="needs-review-translation">Send Coins</target> <context-group purpose="location"><context context-type="linenumber">218</context></context-group> </trans-unit> - <trans-unit id="_msg863"> + <trans-unit id="_msg866"> <source xml:space="preserve">Fee bump error</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">497</context></context-group> @@ -4746,67 +4770,67 @@ Go to File > Open Wallet to load a wallet. <context-group purpose="location"><context context-type="linenumber">562</context></context-group> <context-group purpose="location"><context context-type="linenumber">567</context></context-group> </trans-unit> - <trans-unit id="_msg864"> + <trans-unit id="_msg867"> <source xml:space="preserve">Increasing transaction fee failed</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">497</context></context-group> </trans-unit> - <trans-unit id="_msg865"> + <trans-unit id="_msg868"> <source xml:space="preserve">Do you want to increase the fee?</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">505</context></context-group> </trans-unit> - <trans-unit id="_msg866"> + <trans-unit id="_msg869"> <source xml:space="preserve">Do you want to draft a transaction with fee increase?</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">505</context></context-group> </trans-unit> - <trans-unit id="_msg867"> + <trans-unit id="_msg870"> <source xml:space="preserve">Current fee:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">509</context></context-group> </trans-unit> - <trans-unit id="_msg868"> + <trans-unit id="_msg871"> <source xml:space="preserve">Increase:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">513</context></context-group> </trans-unit> - <trans-unit id="_msg869"> + <trans-unit id="_msg872"> <source xml:space="preserve">New fee:</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">517</context></context-group> </trans-unit> - <trans-unit id="_msg870"> + <trans-unit id="_msg873"> <source xml:space="preserve">Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">525</context></context-group> </trans-unit> - <trans-unit id="_msg871"> + <trans-unit id="_msg874"> <source xml:space="preserve">Confirm fee bump</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">528</context></context-group> </trans-unit> - <trans-unit id="_msg872"> + <trans-unit id="_msg875"> <source xml:space="preserve">Can't draft transaction.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">549</context></context-group> </trans-unit> - <trans-unit id="_msg873"> + <trans-unit id="_msg876"> <source xml:space="preserve">PSBT copied</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">556</context></context-group> </trans-unit> - <trans-unit id="_msg874"> + <trans-unit id="_msg877"> <source xml:space="preserve">Can't sign transaction.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">562</context></context-group> </trans-unit> - <trans-unit id="_msg875"> + <trans-unit id="_msg878"> <source xml:space="preserve">Could not commit transaction</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">567</context></context-group> </trans-unit> - <trans-unit id="_msg876"> + <trans-unit id="_msg879"> <source xml:space="preserve">default wallet</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">587</context></context-group> @@ -4815,872 +4839,847 @@ Go to File > Open Wallet to load a wallet. </body></file> <file original="../walletview.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="WalletView"> - <trans-unit id="_msg877"> + <trans-unit id="_msg880"> <source xml:space="preserve">&Export</source> <target xml:space="preserve" state="needs-review-translation">&Export</target> <context-group purpose="location"><context context-type="linenumber">51</context></context-group> </trans-unit> - <trans-unit id="_msg878"> + <trans-unit id="_msg881"> <source xml:space="preserve">Export the data in the current tab to a file</source> <target xml:space="preserve" state="needs-review-translation">Export the data in the current tab to a file</target> <context-group purpose="location"><context context-type="linenumber">52</context></context-group> </trans-unit> - <trans-unit id="_msg879"> + <trans-unit id="_msg882"> <source xml:space="preserve">Error</source> <target xml:space="preserve" state="needs-review-translation">Error</target> <context-group purpose="location"><context context-type="linenumber">217</context></context-group> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> <context-group purpose="location"><context context-type="linenumber">236</context></context-group> </trans-unit> - <trans-unit id="_msg880"> + <trans-unit id="_msg883"> <source xml:space="preserve">Unable to decode PSBT from clipboard (invalid base64)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">217</context></context-group> </trans-unit> - <trans-unit id="_msg881"> + <trans-unit id="_msg884"> <source xml:space="preserve">Load Transaction Data</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">222</context></context-group> </trans-unit> - <trans-unit id="_msg882"> + <trans-unit id="_msg885"> <source xml:space="preserve">Partially Signed Transaction (*.psbt)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">223</context></context-group> </trans-unit> - <trans-unit id="_msg883"> + <trans-unit id="_msg886"> <source xml:space="preserve">PSBT file must be smaller than 100 MiB</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg884"> + <trans-unit id="_msg887"> <source xml:space="preserve">Unable to decode PSBT</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">236</context></context-group> </trans-unit> - <trans-unit id="_msg885"> + <trans-unit id="_msg888"> <source xml:space="preserve">Backup Wallet</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">275</context></context-group> </trans-unit> - <trans-unit id="_msg886"> + <trans-unit id="_msg889"> <source xml:space="preserve">Wallet Data</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">276</context></context-group> - <context-group><context context-type="x-gettext-msgctxt">Name of wallet data file format</context></context-group> + <context-group purpose="location"><context context-type="linenumber">277</context></context-group> + <note annotates="source" from="developer">Name of the wallet data file format.</note> </trans-unit> - <trans-unit id="_msg887"> + <trans-unit id="_msg890"> <source xml:space="preserve">Backup Failed</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">282</context></context-group> + <context-group purpose="location"><context context-type="linenumber">283</context></context-group> </trans-unit> - <trans-unit id="_msg888"> + <trans-unit id="_msg891"> <source xml:space="preserve">There was an error trying to save the wallet data to %1.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">282</context></context-group> + <context-group purpose="location"><context context-type="linenumber">283</context></context-group> </trans-unit> - <trans-unit id="_msg889"> + <trans-unit id="_msg892"> <source xml:space="preserve">Backup Successful</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">286</context></context-group> + <context-group purpose="location"><context context-type="linenumber">287</context></context-group> </trans-unit> - <trans-unit id="_msg890"> + <trans-unit id="_msg893"> <source xml:space="preserve">The wallet data was successfully saved to %1.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">286</context></context-group> + <context-group purpose="location"><context context-type="linenumber">287</context></context-group> </trans-unit> - <trans-unit id="_msg891"> + <trans-unit id="_msg894"> <source xml:space="preserve">Cancel</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">330</context></context-group> + <context-group purpose="location"><context context-type="linenumber">331</context></context-group> </trans-unit> </group> </body></file> <file original="../bitcoinstrings.cpp" datatype="cpp" source-language="en" target-language="en"><body> <group restype="x-trolltech-linguist-context" resname="bitcoin-core"> - <trans-unit id="_msg892"> + <trans-unit id="_msg895"> <source xml:space="preserve">The %s developers</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">12</context></context-group> </trans-unit> - <trans-unit id="_msg893"> + <trans-unit id="_msg896"> <source xml:space="preserve">%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">13</context></context-group> </trans-unit> - <trans-unit id="_msg894"> + <trans-unit id="_msg897"> <source xml:space="preserve">-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">16</context></context-group> </trans-unit> - <trans-unit id="_msg895"> + <trans-unit id="_msg898"> <source xml:space="preserve">Cannot downgrade wallet from version %i to version %i. Wallet version unchanged.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">19</context></context-group> </trans-unit> - <trans-unit id="_msg896"> + <trans-unit id="_msg899"> <source xml:space="preserve">Cannot obtain a lock on data directory %s. %s is probably already running.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">22</context></context-group> </trans-unit> - <trans-unit id="_msg897"> + <trans-unit id="_msg900"> <source xml:space="preserve">Cannot provide specific connections and have addrman find outgoing connections at the same.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">24</context></context-group> </trans-unit> - <trans-unit id="_msg898"> + <trans-unit id="_msg901"> <source xml:space="preserve">Cannot upgrade a non HD split wallet from version %i to version %i without upgrading to support pre-split keypool. Please use version %i or no version specified.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">27</context></context-group> </trans-unit> - <trans-unit id="_msg899"> + <trans-unit id="_msg902"> <source xml:space="preserve">Distributed under the MIT software license, see the accompanying file %s or %s</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">31</context></context-group> </trans-unit> - <trans-unit id="_msg900"> + <trans-unit id="_msg903"> <source xml:space="preserve">Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">34</context></context-group> </trans-unit> - <trans-unit id="_msg901"> + <trans-unit id="_msg904"> <source xml:space="preserve">Error: Dumpfile format record is incorrect. Got "%s", expected "format".</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">37</context></context-group> </trans-unit> - <trans-unit id="_msg902"> + <trans-unit id="_msg905"> <source xml:space="preserve">Error: Dumpfile identifier record is incorrect. Got "%s", expected "%s".</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">39</context></context-group> </trans-unit> - <trans-unit id="_msg903"> + <trans-unit id="_msg906"> <source xml:space="preserve">Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">41</context></context-group> </trans-unit> - <trans-unit id="_msg904"> + <trans-unit id="_msg907"> <source xml:space="preserve">Error: Listening for incoming connections failed (listen returned error %s)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">44</context></context-group> </trans-unit> - <trans-unit id="_msg905"> + <trans-unit id="_msg908"> <source xml:space="preserve">Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">46</context></context-group> </trans-unit> - <trans-unit id="_msg906"> + <trans-unit id="_msg909"> <source xml:space="preserve">File %s already exists. If you are sure this is what you want, move it out of the way first.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">49</context></context-group> </trans-unit> - <trans-unit id="_msg907"> + <trans-unit id="_msg910"> <source xml:space="preserve">Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">52</context></context-group> </trans-unit> - <trans-unit id="_msg908"> + <trans-unit id="_msg911"> <source xml:space="preserve">More than one onion bind address is provided. Using %s for the automatically created Tor onion service.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">55</context></context-group> </trans-unit> - <trans-unit id="_msg909"> + <trans-unit id="_msg912"> <source xml:space="preserve">No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">58</context></context-group> </trans-unit> - <trans-unit id="_msg910"> + <trans-unit id="_msg913"> <source xml:space="preserve">No dump file provided. To use dump, -dumpfile=<filename> must be provided.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">61</context></context-group> </trans-unit> - <trans-unit id="_msg911"> + <trans-unit id="_msg914"> <source xml:space="preserve">No wallet file format provided. To use createfromdump, -format=<format> must be provided.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">63</context></context-group> </trans-unit> - <trans-unit id="_msg912"> + <trans-unit id="_msg915"> <source xml:space="preserve">Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">66</context></context-group> </trans-unit> - <trans-unit id="_msg913"> + <trans-unit id="_msg916"> <source xml:space="preserve">Please contribute if you find %s useful. Visit %s for further information about the software.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">69</context></context-group> </trans-unit> - <trans-unit id="_msg914"> + <trans-unit id="_msg917"> <source xml:space="preserve">Prune configured below the minimum of %d MiB. Please use a higher number.</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">72</context></context-group> </trans-unit> - <trans-unit id="_msg915"> + <trans-unit id="_msg918"> <source xml:space="preserve">Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source> <target xml:space="preserve"></target> <context-group purpose="location"><context context-type="linenumber">74</context></context-group> </trans-unit> - <trans-unit id="_msg916"> - <source xml:space="preserve">SQLiteDatabase: Failed to prepare the statement to fetch sqlite wallet schema version: %s</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">77</context></context-group> - </trans-unit> - <trans-unit id="_msg917"> - <source xml:space="preserve">SQLiteDatabase: Failed to prepare the statement to fetch the application id: %s</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">80</context></context-group> - </trans-unit> - <trans-unit id="_msg918"> - <source xml:space="preserve">SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">83</context></context-group> - </trans-unit> <trans-unit id="_msg919"> - <source xml:space="preserve">The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source> + <source xml:space="preserve">SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">86</context></context-group> + <context-group purpose="location"><context context-type="linenumber">77</context></context-group> </trans-unit> <trans-unit id="_msg920"> - <source xml:space="preserve">The transaction amount is too small to send after the fee has been deducted</source> + <source xml:space="preserve">The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">91</context></context-group> + <context-group purpose="location"><context context-type="linenumber">80</context></context-group> </trans-unit> <trans-unit id="_msg921"> - <source xml:space="preserve">This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source> + <source xml:space="preserve">The transaction amount is too small to send after the fee has been deducted</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">93</context></context-group> + <context-group purpose="location"><context context-type="linenumber">85</context></context-group> </trans-unit> <trans-unit id="_msg922"> - <source xml:space="preserve">This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source> + <source xml:space="preserve">This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">97</context></context-group> + <context-group purpose="location"><context context-type="linenumber">87</context></context-group> </trans-unit> <trans-unit id="_msg923"> - <source xml:space="preserve">This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source> + <source xml:space="preserve">This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">100</context></context-group> + <context-group purpose="location"><context context-type="linenumber">91</context></context-group> </trans-unit> <trans-unit id="_msg924"> - <source xml:space="preserve">This is the transaction fee you may discard if change is smaller than dust at this level</source> + <source xml:space="preserve">This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">103</context></context-group> + <context-group purpose="location"><context context-type="linenumber">94</context></context-group> </trans-unit> <trans-unit id="_msg925"> - <source xml:space="preserve">This is the transaction fee you may pay when fee estimates are not available.</source> + <source xml:space="preserve">This is the transaction fee you may discard if change is smaller than dust at this level</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">106</context></context-group> + <context-group purpose="location"><context context-type="linenumber">97</context></context-group> </trans-unit> <trans-unit id="_msg926"> - <source xml:space="preserve">Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source> + <source xml:space="preserve">This is the transaction fee you may pay when fee estimates are not available.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">108</context></context-group> + <context-group purpose="location"><context context-type="linenumber">100</context></context-group> </trans-unit> <trans-unit id="_msg927"> - <source xml:space="preserve">Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.</source> + <source xml:space="preserve">Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">111</context></context-group> + <context-group purpose="location"><context context-type="linenumber">102</context></context-group> </trans-unit> <trans-unit id="_msg928"> - <source xml:space="preserve">Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source> + <source xml:space="preserve">Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">114</context></context-group> + <context-group purpose="location"><context context-type="linenumber">105</context></context-group> </trans-unit> <trans-unit id="_msg929"> - <source xml:space="preserve">Unknown wallet file format "%s" provided. Please provide one of "bdb" or "sqlite".</source> + <source xml:space="preserve">Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">117</context></context-group> + <context-group purpose="location"><context context-type="linenumber">108</context></context-group> </trans-unit> <trans-unit id="_msg930"> - <source xml:space="preserve">Warning: Dumpfile wallet format "%s" does not match command line specified format "%s".</source> + <source xml:space="preserve">Unknown wallet file format "%s" provided. Please provide one of "bdb" or "sqlite".</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">120</context></context-group> + <context-group purpose="location"><context context-type="linenumber">111</context></context-group> </trans-unit> <trans-unit id="_msg931"> - <source xml:space="preserve">Warning: Private keys detected in wallet {%s} with disabled private keys</source> + <source xml:space="preserve">Warning: Dumpfile wallet format "%s" does not match command line specified format "%s".</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">123</context></context-group> + <context-group purpose="location"><context context-type="linenumber">114</context></context-group> </trans-unit> <trans-unit id="_msg932"> - <source xml:space="preserve">Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source> + <source xml:space="preserve">Warning: Private keys detected in wallet {%s} with disabled private keys</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">125</context></context-group> + <context-group purpose="location"><context context-type="linenumber">117</context></context-group> </trans-unit> <trans-unit id="_msg933"> - <source xml:space="preserve">Witness data for blocks after height %d requires validation. Please restart with -reindex.</source> + <source xml:space="preserve">Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">128</context></context-group> + <context-group purpose="location"><context context-type="linenumber">119</context></context-group> </trans-unit> <trans-unit id="_msg934"> - <source xml:space="preserve">You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> + <source xml:space="preserve">Witness data for blocks after height %d requires validation. Please restart with -reindex.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">131</context></context-group> + <context-group purpose="location"><context context-type="linenumber">122</context></context-group> </trans-unit> <trans-unit id="_msg935"> - <source xml:space="preserve">%s is set very high!</source> + <source xml:space="preserve">You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">134</context></context-group> + <context-group purpose="location"><context context-type="linenumber">125</context></context-group> </trans-unit> <trans-unit id="_msg936"> - <source xml:space="preserve">-maxmempool must be at least %d MB</source> + <source xml:space="preserve">%s is set very high!</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">135</context></context-group> + <context-group purpose="location"><context context-type="linenumber">128</context></context-group> </trans-unit> <trans-unit id="_msg937"> - <source xml:space="preserve">A fatal internal error occurred, see debug.log for details</source> + <source xml:space="preserve">-maxmempool must be at least %d MB</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">136</context></context-group> + <context-group purpose="location"><context context-type="linenumber">129</context></context-group> </trans-unit> <trans-unit id="_msg938"> - <source xml:space="preserve">Cannot resolve -%s address: '%s'</source> + <source xml:space="preserve">A fatal internal error occurred, see debug.log for details</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">137</context></context-group> + <context-group purpose="location"><context context-type="linenumber">130</context></context-group> </trans-unit> <trans-unit id="_msg939"> - <source xml:space="preserve">Cannot set -peerblockfilters without -blockfilterindex.</source> + <source xml:space="preserve">Cannot resolve -%s address: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">138</context></context-group> + <context-group purpose="location"><context context-type="linenumber">131</context></context-group> </trans-unit> <trans-unit id="_msg940"> - <source xml:space="preserve">Cannot write to data directory '%s'; check permissions.</source> + <source xml:space="preserve">Cannot set -peerblockfilters without -blockfilterindex.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">139</context></context-group> + <context-group purpose="location"><context context-type="linenumber">132</context></context-group> </trans-unit> <trans-unit id="_msg941"> - <source xml:space="preserve">Change index out of range</source> + <source xml:space="preserve">Cannot write to data directory '%s'; check permissions.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">140</context></context-group> + <context-group purpose="location"><context context-type="linenumber">133</context></context-group> </trans-unit> <trans-unit id="_msg942"> - <source xml:space="preserve">Config setting for %s only applied on %s network when in [%s] section.</source> + <source xml:space="preserve">Change index out of range</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">141</context></context-group> + <context-group purpose="location"><context context-type="linenumber">134</context></context-group> </trans-unit> <trans-unit id="_msg943"> - <source xml:space="preserve">Copyright (C) %i-%i</source> + <source xml:space="preserve">Config setting for %s only applied on %s network when in [%s] section.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">142</context></context-group> + <context-group purpose="location"><context context-type="linenumber">135</context></context-group> </trans-unit> <trans-unit id="_msg944"> - <source xml:space="preserve">Corrupted block database detected</source> + <source xml:space="preserve">Copyright (C) %i-%i</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">143</context></context-group> + <context-group purpose="location"><context context-type="linenumber">136</context></context-group> </trans-unit> <trans-unit id="_msg945"> - <source xml:space="preserve">Could not find asmap file %s</source> + <source xml:space="preserve">Corrupted block database detected</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">144</context></context-group> + <context-group purpose="location"><context context-type="linenumber">137</context></context-group> </trans-unit> <trans-unit id="_msg946"> - <source xml:space="preserve">Could not parse asmap file %s</source> + <source xml:space="preserve">Could not find asmap file %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">145</context></context-group> + <context-group purpose="location"><context context-type="linenumber">138</context></context-group> </trans-unit> <trans-unit id="_msg947"> - <source xml:space="preserve">Disk space is too low!</source> + <source xml:space="preserve">Could not parse asmap file %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">146</context></context-group> + <context-group purpose="location"><context context-type="linenumber">139</context></context-group> </trans-unit> <trans-unit id="_msg948"> - <source xml:space="preserve">Do you want to rebuild the block database now?</source> + <source xml:space="preserve">Disk space is too low!</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">147</context></context-group> + <context-group purpose="location"><context context-type="linenumber">140</context></context-group> </trans-unit> <trans-unit id="_msg949"> - <source xml:space="preserve">Done loading</source> + <source xml:space="preserve">Do you want to rebuild the block database now?</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">148</context></context-group> + <context-group purpose="location"><context context-type="linenumber">141</context></context-group> </trans-unit> <trans-unit id="_msg950"> - <source xml:space="preserve">Dump file %s does not exist.</source> + <source xml:space="preserve">Done loading</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">149</context></context-group> + <context-group purpose="location"><context context-type="linenumber">142</context></context-group> </trans-unit> <trans-unit id="_msg951"> - <source xml:space="preserve">Error creating %s</source> + <source xml:space="preserve">Dump file %s does not exist.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">150</context></context-group> + <context-group purpose="location"><context context-type="linenumber">143</context></context-group> </trans-unit> <trans-unit id="_msg952"> - <source xml:space="preserve">Error initializing block database</source> + <source xml:space="preserve">Error creating %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">151</context></context-group> + <context-group purpose="location"><context context-type="linenumber">144</context></context-group> </trans-unit> <trans-unit id="_msg953"> - <source xml:space="preserve">Error initializing wallet database environment %s!</source> + <source xml:space="preserve">Error initializing block database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">152</context></context-group> + <context-group purpose="location"><context context-type="linenumber">145</context></context-group> </trans-unit> <trans-unit id="_msg954"> - <source xml:space="preserve">Error loading %s</source> + <source xml:space="preserve">Error initializing wallet database environment %s!</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">153</context></context-group> + <context-group purpose="location"><context context-type="linenumber">146</context></context-group> </trans-unit> <trans-unit id="_msg955"> - <source xml:space="preserve">Error loading %s: Private keys can only be disabled during creation</source> + <source xml:space="preserve">Error loading %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">154</context></context-group> + <context-group purpose="location"><context context-type="linenumber">147</context></context-group> </trans-unit> <trans-unit id="_msg956"> - <source xml:space="preserve">Error loading %s: Wallet corrupted</source> + <source xml:space="preserve">Error loading %s: Private keys can only be disabled during creation</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">155</context></context-group> + <context-group purpose="location"><context context-type="linenumber">148</context></context-group> </trans-unit> <trans-unit id="_msg957"> - <source xml:space="preserve">Error loading %s: Wallet requires newer version of %s</source> + <source xml:space="preserve">Error loading %s: Wallet corrupted</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">156</context></context-group> + <context-group purpose="location"><context context-type="linenumber">149</context></context-group> </trans-unit> <trans-unit id="_msg958"> - <source xml:space="preserve">Error loading block database</source> + <source xml:space="preserve">Error loading %s: Wallet requires newer version of %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">157</context></context-group> + <context-group purpose="location"><context context-type="linenumber">150</context></context-group> </trans-unit> <trans-unit id="_msg959"> - <source xml:space="preserve">Error opening block database</source> + <source xml:space="preserve">Error loading block database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">158</context></context-group> + <context-group purpose="location"><context context-type="linenumber">151</context></context-group> </trans-unit> <trans-unit id="_msg960"> - <source xml:space="preserve">Error reading from database, shutting down.</source> + <source xml:space="preserve">Error opening block database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">159</context></context-group> + <context-group purpose="location"><context context-type="linenumber">152</context></context-group> </trans-unit> <trans-unit id="_msg961"> - <source xml:space="preserve">Error reading next record from wallet database</source> + <source xml:space="preserve">Error reading from database, shutting down.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">160</context></context-group> + <context-group purpose="location"><context context-type="linenumber">153</context></context-group> </trans-unit> <trans-unit id="_msg962"> - <source xml:space="preserve">Error upgrading chainstate database</source> + <source xml:space="preserve">Error reading next record from wallet database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">161</context></context-group> + <context-group purpose="location"><context context-type="linenumber">154</context></context-group> </trans-unit> <trans-unit id="_msg963"> - <source xml:space="preserve">Error: Couldn't create cursor into database</source> + <source xml:space="preserve">Error upgrading chainstate database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">162</context></context-group> + <context-group purpose="location"><context context-type="linenumber">155</context></context-group> </trans-unit> <trans-unit id="_msg964"> - <source xml:space="preserve">Error: Disk space is low for %s</source> + <source xml:space="preserve">Error: Couldn't create cursor into database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">163</context></context-group> + <context-group purpose="location"><context context-type="linenumber">156</context></context-group> </trans-unit> <trans-unit id="_msg965"> - <source xml:space="preserve">Error: Dumpfile checksum does not match. Computed %s, expected %s</source> + <source xml:space="preserve">Error: Disk space is low for %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">164</context></context-group> + <context-group purpose="location"><context context-type="linenumber">157</context></context-group> </trans-unit> <trans-unit id="_msg966"> - <source xml:space="preserve">Error: Got key that was not hex: %s</source> + <source xml:space="preserve">Error: Dumpfile checksum does not match. Computed %s, expected %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">165</context></context-group> + <context-group purpose="location"><context context-type="linenumber">158</context></context-group> </trans-unit> <trans-unit id="_msg967"> - <source xml:space="preserve">Error: Got value that was not hex: %s</source> + <source xml:space="preserve">Error: Got key that was not hex: %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">166</context></context-group> + <context-group purpose="location"><context context-type="linenumber">159</context></context-group> </trans-unit> <trans-unit id="_msg968"> - <source xml:space="preserve">Error: Keypool ran out, please call keypoolrefill first</source> + <source xml:space="preserve">Error: Got value that was not hex: %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">167</context></context-group> + <context-group purpose="location"><context context-type="linenumber">160</context></context-group> </trans-unit> <trans-unit id="_msg969"> - <source xml:space="preserve">Error: Missing checksum</source> + <source xml:space="preserve">Error: Keypool ran out, please call keypoolrefill first</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">168</context></context-group> + <context-group purpose="location"><context context-type="linenumber">161</context></context-group> </trans-unit> <trans-unit id="_msg970"> - <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source> + <source xml:space="preserve">Error: Missing checksum</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">169</context></context-group> + <context-group purpose="location"><context context-type="linenumber">162</context></context-group> </trans-unit> <trans-unit id="_msg971"> - <source xml:space="preserve">Error: Unable to write record to new wallet</source> + <source xml:space="preserve">Error: Unable to parse version %u as a uint32_t</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">170</context></context-group> + <context-group purpose="location"><context context-type="linenumber">163</context></context-group> </trans-unit> <trans-unit id="_msg972"> - <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source> + <source xml:space="preserve">Error: Unable to write record to new wallet</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">171</context></context-group> + <context-group purpose="location"><context context-type="linenumber">164</context></context-group> </trans-unit> <trans-unit id="_msg973"> - <source xml:space="preserve">Failed to rescan the wallet during initialization</source> + <source xml:space="preserve">Failed to listen on any port. Use -listen=0 if you want this.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">172</context></context-group> + <context-group purpose="location"><context context-type="linenumber">165</context></context-group> </trans-unit> <trans-unit id="_msg974"> - <source xml:space="preserve">Failed to verify database</source> + <source xml:space="preserve">Failed to rescan the wallet during initialization</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">173</context></context-group> + <context-group purpose="location"><context context-type="linenumber">166</context></context-group> </trans-unit> <trans-unit id="_msg975"> - <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source> + <source xml:space="preserve">Failed to verify database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">174</context></context-group> + <context-group purpose="location"><context context-type="linenumber">167</context></context-group> </trans-unit> <trans-unit id="_msg976"> - <source xml:space="preserve">Ignoring duplicate -wallet %s.</source> + <source xml:space="preserve">Fee rate (%s) is lower than the minimum fee rate setting (%s)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">175</context></context-group> + <context-group purpose="location"><context context-type="linenumber">168</context></context-group> </trans-unit> <trans-unit id="_msg977"> - <source xml:space="preserve">Importing…</source> + <source xml:space="preserve">Ignoring duplicate -wallet %s.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">176</context></context-group> + <context-group purpose="location"><context context-type="linenumber">169</context></context-group> </trans-unit> <trans-unit id="_msg978"> - <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source> + <source xml:space="preserve">Importing…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">177</context></context-group> + <context-group purpose="location"><context context-type="linenumber">170</context></context-group> </trans-unit> <trans-unit id="_msg979"> - <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source> + <source xml:space="preserve">Incorrect or no genesis block found. Wrong datadir for network?</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">178</context></context-group> + <context-group purpose="location"><context context-type="linenumber">171</context></context-group> </trans-unit> <trans-unit id="_msg980"> - <source xml:space="preserve">Insufficient funds</source> + <source xml:space="preserve">Initialization sanity check failed. %s is shutting down.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">179</context></context-group> + <context-group purpose="location"><context context-type="linenumber">172</context></context-group> </trans-unit> <trans-unit id="_msg981"> - <source xml:space="preserve">Invalid -i2psam address or hostname: '%s'</source> + <source xml:space="preserve">Insufficient funds</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">180</context></context-group> + <context-group purpose="location"><context context-type="linenumber">173</context></context-group> </trans-unit> <trans-unit id="_msg982"> - <source xml:space="preserve">Invalid -onion address or hostname: '%s'</source> + <source xml:space="preserve">Invalid -i2psam address or hostname: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">181</context></context-group> + <context-group purpose="location"><context context-type="linenumber">174</context></context-group> </trans-unit> <trans-unit id="_msg983"> - <source xml:space="preserve">Invalid -proxy address or hostname: '%s'</source> + <source xml:space="preserve">Invalid -onion address or hostname: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">182</context></context-group> + <context-group purpose="location"><context context-type="linenumber">175</context></context-group> </trans-unit> <trans-unit id="_msg984"> - <source xml:space="preserve">Invalid P2P permission: '%s'</source> + <source xml:space="preserve">Invalid -proxy address or hostname: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">183</context></context-group> + <context-group purpose="location"><context context-type="linenumber">176</context></context-group> </trans-unit> <trans-unit id="_msg985"> - <source xml:space="preserve">Invalid amount for -%s=<amount>: '%s'</source> + <source xml:space="preserve">Invalid P2P permission: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">184</context></context-group> + <context-group purpose="location"><context context-type="linenumber">177</context></context-group> </trans-unit> <trans-unit id="_msg986"> - <source xml:space="preserve">Invalid amount for -discardfee=<amount>: '%s'</source> + <source xml:space="preserve">Invalid amount for -%s=<amount>: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">185</context></context-group> + <context-group purpose="location"><context context-type="linenumber">178</context></context-group> </trans-unit> <trans-unit id="_msg987"> - <source xml:space="preserve">Invalid amount for -fallbackfee=<amount>: '%s'</source> + <source xml:space="preserve">Invalid amount for -discardfee=<amount>: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">186</context></context-group> + <context-group purpose="location"><context context-type="linenumber">179</context></context-group> </trans-unit> <trans-unit id="_msg988"> - <source xml:space="preserve">Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)</source> + <source xml:space="preserve">Invalid amount for -fallbackfee=<amount>: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">187</context></context-group> + <context-group purpose="location"><context context-type="linenumber">180</context></context-group> </trans-unit> <trans-unit id="_msg989"> - <source xml:space="preserve">Invalid netmask specified in -whitelist: '%s'</source> + <source xml:space="preserve">Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">188</context></context-group> + <context-group purpose="location"><context context-type="linenumber">181</context></context-group> </trans-unit> <trans-unit id="_msg990"> - <source xml:space="preserve">Loading P2P addresses…</source> + <source xml:space="preserve">Invalid netmask specified in -whitelist: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">189</context></context-group> + <context-group purpose="location"><context context-type="linenumber">182</context></context-group> </trans-unit> <trans-unit id="_msg991"> - <source xml:space="preserve">Loading banlist…</source> + <source xml:space="preserve">Loading P2P addresses…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">190</context></context-group> + <context-group purpose="location"><context context-type="linenumber">183</context></context-group> </trans-unit> <trans-unit id="_msg992"> - <source xml:space="preserve">Loading block index…</source> + <source xml:space="preserve">Loading banlist…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">191</context></context-group> + <context-group purpose="location"><context context-type="linenumber">184</context></context-group> </trans-unit> <trans-unit id="_msg993"> - <source xml:space="preserve">Loading wallet…</source> + <source xml:space="preserve">Loading block index…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">192</context></context-group> + <context-group purpose="location"><context context-type="linenumber">185</context></context-group> </trans-unit> <trans-unit id="_msg994"> - <source xml:space="preserve">Need to specify a port with -whitebind: '%s'</source> + <source xml:space="preserve">Loading wallet…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">193</context></context-group> + <context-group purpose="location"><context context-type="linenumber">186</context></context-group> </trans-unit> <trans-unit id="_msg995"> - <source xml:space="preserve">No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>.</source> + <source xml:space="preserve">Need to specify a port with -whitebind: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">194</context></context-group> + <context-group purpose="location"><context context-type="linenumber">187</context></context-group> </trans-unit> <trans-unit id="_msg996"> - <source xml:space="preserve">Not enough file descriptors available.</source> + <source xml:space="preserve">No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">195</context></context-group> + <context-group purpose="location"><context context-type="linenumber">188</context></context-group> </trans-unit> <trans-unit id="_msg997"> - <source xml:space="preserve">Prune cannot be configured with a negative value.</source> + <source xml:space="preserve">Not enough file descriptors available.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">196</context></context-group> + <context-group purpose="location"><context context-type="linenumber">189</context></context-group> </trans-unit> <trans-unit id="_msg998"> - <source xml:space="preserve">Prune mode is incompatible with -coinstatsindex.</source> + <source xml:space="preserve">Prune cannot be configured with a negative value.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">197</context></context-group> + <context-group purpose="location"><context context-type="linenumber">190</context></context-group> </trans-unit> <trans-unit id="_msg999"> - <source xml:space="preserve">Prune mode is incompatible with -txindex.</source> + <source xml:space="preserve">Prune mode is incompatible with -coinstatsindex.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">198</context></context-group> + <context-group purpose="location"><context context-type="linenumber">191</context></context-group> </trans-unit> <trans-unit id="_msg1000"> - <source xml:space="preserve">Pruning blockstore…</source> + <source xml:space="preserve">Prune mode is incompatible with -txindex.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">199</context></context-group> + <context-group purpose="location"><context context-type="linenumber">192</context></context-group> </trans-unit> <trans-unit id="_msg1001"> - <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source> + <source xml:space="preserve">Pruning blockstore…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">200</context></context-group> + <context-group purpose="location"><context context-type="linenumber">193</context></context-group> </trans-unit> <trans-unit id="_msg1002"> - <source xml:space="preserve">Replaying blocks…</source> + <source xml:space="preserve">Reducing -maxconnections from %d to %d, because of system limitations.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">201</context></context-group> + <context-group purpose="location"><context context-type="linenumber">194</context></context-group> </trans-unit> <trans-unit id="_msg1003"> - <source xml:space="preserve">Rescanning…</source> + <source xml:space="preserve">Replaying blocks…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">202</context></context-group> + <context-group purpose="location"><context context-type="linenumber">195</context></context-group> </trans-unit> <trans-unit id="_msg1004"> - <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source> + <source xml:space="preserve">Rescanning…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">203</context></context-group> + <context-group purpose="location"><context context-type="linenumber">196</context></context-group> </trans-unit> <trans-unit id="_msg1005"> - <source xml:space="preserve">SQLiteDatabase: Failed to fetch sqlite wallet schema version: %s</source> + <source xml:space="preserve">SQLiteDatabase: Failed to execute statement to verify database: %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">204</context></context-group> + <context-group purpose="location"><context context-type="linenumber">197</context></context-group> </trans-unit> <trans-unit id="_msg1006"> - <source xml:space="preserve">SQLiteDatabase: Failed to fetch the application id: %s</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">205</context></context-group> - </trans-unit> - <trans-unit id="_msg1007"> <source xml:space="preserve">SQLiteDatabase: Failed to prepare statement to verify database: %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">206</context></context-group> + <context-group purpose="location"><context context-type="linenumber">198</context></context-group> </trans-unit> - <trans-unit id="_msg1008"> + <trans-unit id="_msg1007"> <source xml:space="preserve">SQLiteDatabase: Failed to read database verification error: %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">207</context></context-group> + <context-group purpose="location"><context context-type="linenumber">199</context></context-group> </trans-unit> - <trans-unit id="_msg1009"> + <trans-unit id="_msg1008"> <source xml:space="preserve">SQLiteDatabase: Unexpected application id. Expected %u, got %u</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">208</context></context-group> + <context-group purpose="location"><context context-type="linenumber">200</context></context-group> </trans-unit> - <trans-unit id="_msg1010"> + <trans-unit id="_msg1009"> <source xml:space="preserve">Section [%s] is not recognized.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">209</context></context-group> + <context-group purpose="location"><context context-type="linenumber">201</context></context-group> </trans-unit> - <trans-unit id="_msg1011"> + <trans-unit id="_msg1010"> <source xml:space="preserve">Signing transaction failed</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">210</context></context-group> + <context-group purpose="location"><context context-type="linenumber">202</context></context-group> </trans-unit> - <trans-unit id="_msg1012"> + <trans-unit id="_msg1011"> <source xml:space="preserve">Specified -walletdir "%s" does not exist</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">211</context></context-group> + <context-group purpose="location"><context context-type="linenumber">203</context></context-group> </trans-unit> - <trans-unit id="_msg1013"> + <trans-unit id="_msg1012"> <source xml:space="preserve">Specified -walletdir "%s" is a relative path</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">212</context></context-group> + <context-group purpose="location"><context context-type="linenumber">204</context></context-group> </trans-unit> - <trans-unit id="_msg1014"> + <trans-unit id="_msg1013"> <source xml:space="preserve">Specified -walletdir "%s" is not a directory</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">213</context></context-group> + <context-group purpose="location"><context context-type="linenumber">205</context></context-group> </trans-unit> - <trans-unit id="_msg1015"> + <trans-unit id="_msg1014"> <source xml:space="preserve">Specified blocks directory "%s" does not exist.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">214</context></context-group> + <context-group purpose="location"><context context-type="linenumber">206</context></context-group> </trans-unit> - <trans-unit id="_msg1016"> + <trans-unit id="_msg1015"> <source xml:space="preserve">Starting network threads…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">215</context></context-group> + <context-group purpose="location"><context context-type="linenumber">207</context></context-group> </trans-unit> - <trans-unit id="_msg1017"> + <trans-unit id="_msg1016"> <source xml:space="preserve">The source code is available from %s.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">216</context></context-group> + <context-group purpose="location"><context context-type="linenumber">208</context></context-group> </trans-unit> - <trans-unit id="_msg1018"> + <trans-unit id="_msg1017"> <source xml:space="preserve">The specified config file %s does not exist</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">217</context></context-group> + <context-group purpose="location"><context context-type="linenumber">209</context></context-group> </trans-unit> - <trans-unit id="_msg1019"> + <trans-unit id="_msg1018"> <source xml:space="preserve">The transaction amount is too small to pay the fee</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">218</context></context-group> + <context-group purpose="location"><context context-type="linenumber">210</context></context-group> </trans-unit> - <trans-unit id="_msg1020"> + <trans-unit id="_msg1019"> <source xml:space="preserve">The wallet will avoid paying less than the minimum relay fee.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">219</context></context-group> + <context-group purpose="location"><context context-type="linenumber">211</context></context-group> </trans-unit> - <trans-unit id="_msg1021"> + <trans-unit id="_msg1020"> <source xml:space="preserve">This is experimental software.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">220</context></context-group> + <context-group purpose="location"><context context-type="linenumber">212</context></context-group> </trans-unit> - <trans-unit id="_msg1022"> + <trans-unit id="_msg1021"> <source xml:space="preserve">This is the minimum transaction fee you pay on every transaction.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">221</context></context-group> + <context-group purpose="location"><context context-type="linenumber">213</context></context-group> </trans-unit> - <trans-unit id="_msg1023"> + <trans-unit id="_msg1022"> <source xml:space="preserve">This is the transaction fee you will pay if you send a transaction.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">222</context></context-group> + <context-group purpose="location"><context context-type="linenumber">214</context></context-group> </trans-unit> - <trans-unit id="_msg1024"> + <trans-unit id="_msg1023"> <source xml:space="preserve">Transaction amount too small</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">223</context></context-group> + <context-group purpose="location"><context context-type="linenumber">215</context></context-group> </trans-unit> - <trans-unit id="_msg1025"> + <trans-unit id="_msg1024"> <source xml:space="preserve">Transaction amounts must not be negative</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">224</context></context-group> - </trans-unit> - <trans-unit id="_msg1026"> - <source xml:space="preserve">Transaction fee and change calculation failed</source> - <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">225</context></context-group> + <context-group purpose="location"><context context-type="linenumber">216</context></context-group> </trans-unit> - <trans-unit id="_msg1027"> + <trans-unit id="_msg1025"> <source xml:space="preserve">Transaction has too long of a mempool chain</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">226</context></context-group> + <context-group purpose="location"><context context-type="linenumber">217</context></context-group> </trans-unit> - <trans-unit id="_msg1028"> + <trans-unit id="_msg1026"> <source xml:space="preserve">Transaction must have at least one recipient</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">227</context></context-group> + <context-group purpose="location"><context context-type="linenumber">218</context></context-group> </trans-unit> - <trans-unit id="_msg1029"> + <trans-unit id="_msg1027"> <source xml:space="preserve">Transaction too large</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">228</context></context-group> + <context-group purpose="location"><context context-type="linenumber">219</context></context-group> </trans-unit> - <trans-unit id="_msg1030"> + <trans-unit id="_msg1028"> <source xml:space="preserve">Unable to bind to %s on this computer (bind returned error %s)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">229</context></context-group> + <context-group purpose="location"><context context-type="linenumber">220</context></context-group> </trans-unit> - <trans-unit id="_msg1031"> + <trans-unit id="_msg1029"> <source xml:space="preserve">Unable to bind to %s on this computer. %s is probably already running.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">230</context></context-group> + <context-group purpose="location"><context context-type="linenumber">221</context></context-group> </trans-unit> - <trans-unit id="_msg1032"> + <trans-unit id="_msg1030"> <source xml:space="preserve">Unable to create the PID file '%s': %s</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">231</context></context-group> + <context-group purpose="location"><context context-type="linenumber">222</context></context-group> </trans-unit> - <trans-unit id="_msg1033"> + <trans-unit id="_msg1031"> <source xml:space="preserve">Unable to generate initial keys</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">232</context></context-group> + <context-group purpose="location"><context context-type="linenumber">223</context></context-group> </trans-unit> - <trans-unit id="_msg1034"> + <trans-unit id="_msg1032"> <source xml:space="preserve">Unable to generate keys</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">233</context></context-group> + <context-group purpose="location"><context context-type="linenumber">224</context></context-group> </trans-unit> - <trans-unit id="_msg1035"> + <trans-unit id="_msg1033"> <source xml:space="preserve">Unable to open %s for writing</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">234</context></context-group> + <context-group purpose="location"><context context-type="linenumber">225</context></context-group> </trans-unit> - <trans-unit id="_msg1036"> + <trans-unit id="_msg1034"> <source xml:space="preserve">Unable to start HTTP server. See debug log for details.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">235</context></context-group> + <context-group purpose="location"><context context-type="linenumber">226</context></context-group> </trans-unit> - <trans-unit id="_msg1037"> + <trans-unit id="_msg1035"> <source xml:space="preserve">Unknown -blockfilterindex value %s.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">236</context></context-group> + <context-group purpose="location"><context context-type="linenumber">227</context></context-group> </trans-unit> - <trans-unit id="_msg1038"> + <trans-unit id="_msg1036"> <source xml:space="preserve">Unknown address type '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">237</context></context-group> + <context-group purpose="location"><context context-type="linenumber">228</context></context-group> </trans-unit> - <trans-unit id="_msg1039"> + <trans-unit id="_msg1037"> <source xml:space="preserve">Unknown change type '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">238</context></context-group> + <context-group purpose="location"><context context-type="linenumber">229</context></context-group> </trans-unit> - <trans-unit id="_msg1040"> + <trans-unit id="_msg1038"> <source xml:space="preserve">Unknown network specified in -onlynet: '%s'</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">239</context></context-group> + <context-group purpose="location"><context context-type="linenumber">230</context></context-group> </trans-unit> - <trans-unit id="_msg1041"> + <trans-unit id="_msg1039"> <source xml:space="preserve">Unsupported logging category %s=%s.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">240</context></context-group> + <context-group purpose="location"><context context-type="linenumber">231</context></context-group> </trans-unit> - <trans-unit id="_msg1042"> + <trans-unit id="_msg1040"> <source xml:space="preserve">Upgrading UTXO database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">241</context></context-group> + <context-group purpose="location"><context context-type="linenumber">232</context></context-group> </trans-unit> - <trans-unit id="_msg1043"> + <trans-unit id="_msg1041"> <source xml:space="preserve">Upgrading txindex database</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">242</context></context-group> + <context-group purpose="location"><context context-type="linenumber">233</context></context-group> </trans-unit> - <trans-unit id="_msg1044"> + <trans-unit id="_msg1042"> <source xml:space="preserve">User Agent comment (%s) contains unsafe characters.</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">243</context></context-group> + <context-group purpose="location"><context context-type="linenumber">234</context></context-group> </trans-unit> - <trans-unit id="_msg1045"> + <trans-unit id="_msg1043"> <source xml:space="preserve">Verifying blocks…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">244</context></context-group> + <context-group purpose="location"><context context-type="linenumber">235</context></context-group> </trans-unit> - <trans-unit id="_msg1046"> + <trans-unit id="_msg1044"> <source xml:space="preserve">Verifying wallet(s)…</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">245</context></context-group> + <context-group purpose="location"><context context-type="linenumber">236</context></context-group> </trans-unit> - <trans-unit id="_msg1047"> + <trans-unit id="_msg1045"> <source xml:space="preserve">Wallet needed to be rewritten: restart %s to complete</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">246</context></context-group> + <context-group purpose="location"><context context-type="linenumber">237</context></context-group> </trans-unit> - <trans-unit id="_msg1048"> + <trans-unit id="_msg1046"> <source xml:space="preserve">Warning: unknown new rules activated (versionbit %i)</source> <target xml:space="preserve"></target> - <context-group purpose="location"><context context-type="linenumber">247</context></context-group> + <context-group purpose="location"><context context-type="linenumber">238</context></context-group> </trans-unit> </group> </body></file> diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 70762ab6bc..abdf9e9ae6 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -21,6 +21,7 @@ #include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS #include <QDebug> +#include <QLatin1Char> #include <QSettings> #include <QStringList> @@ -244,7 +245,7 @@ static ProxySetting GetProxySetting(QSettings &settings, const QString &name) static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port) { - settings.setValue(name, ip_port.ip + ":" + ip_port.port); + settings.setValue(name, QString{ip_port.ip + QLatin1Char(':') + ip_port.port}); } static const QString GetDefaultProxyAddress() diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 7f12b1d2b5..27783bdf87 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -78,6 +78,7 @@ public: { QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole)); QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight); + iconWatchonly = platformStyle->TextColorIcon(iconWatchonly); iconWatchonly.paint(painter, watchonlyRect); address_rect_min_width += 5 + watchonlyRect.width(); } @@ -143,6 +144,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) ui(new Ui::OverviewPage), clientModel(nullptr), walletModel(nullptr), + m_platform_style{platformStyle}, txdelegate(new TxViewDelegate(platformStyle, this)) { ui->setupUi(this); @@ -150,7 +152,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) m_balances.balance = -1; // use a SingleColorIcon for the "out of sync warning" icon - QIcon icon = platformStyle->SingleColorIcon(":/icons/warning"); + QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning")); ui->labelTransactionsStatus->setIcon(icon); ui->labelWalletStatus->setIcon(icon); @@ -298,6 +300,17 @@ void OverviewPage::setWalletModel(WalletModel *model) updateDisplayUnit(); } +void OverviewPage::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning")); + ui->labelTransactionsStatus->setIcon(icon); + ui->labelWalletStatus->setIcon(icon); + } +#endif +} + void OverviewPage::updateDisplayUnit() { if(walletModel && walletModel->getOptionsModel()) diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 5158c81678..755a107a00 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -45,6 +45,9 @@ Q_SIGNALS: void transactionClicked(const QModelIndex &index); void outOfSyncWarningClicked(); +protected: + void changeEvent(QEvent* e) override; + private: Ui::OverviewPage *ui; ClientModel *clientModel; @@ -52,6 +55,8 @@ private: interfaces::WalletBalances m_balances; bool m_privacy{false}; + const PlatformStyle* m_platform_style; + TxViewDelegate *txdelegate; std::unique_ptr<TransactionFilterProxy> filter; diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 6c4e326011..11441481bb 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -114,7 +114,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const return (qint64)rec->nodeStats.nodeid; case Address: // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection - return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName); + return QString::fromStdString((rec->nodeStats.fInbound ? "↓ " : "↑ ") + rec->nodeStats.addrName); case ConnectionType: return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false); case Network: diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index 9c7bc25da2..3d195342f1 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -74,7 +74,31 @@ public Q_SLOTS: private: interfaces::Node& m_node; - const QStringList columns{tr("Peer Id"), tr("Address"), tr("Type"), tr("Network"), tr("Ping"), tr("Sent"), tr("Received"), tr("User Agent")}; + const QStringList columns{ + /*: Title of Peers Table column which contains a + unique number used to identify a connection. */ + tr("Peer"), + /*: Title of Peers Table column which contains the + IP/Onion/I2P address of the connected peer. */ + tr("Address"), + /*: Title of Peers Table column which describes the type of + peer connection. The "type" describes why the connection exists. */ + tr("Type"), + /*: Title of Peers Table column which states the network the peer + connected through. */ + tr("Network"), + /*: Title of Peers Table column which indicates the current latency + of the connection with the peer. */ + tr("Ping"), + /*: Title of Peers Table column which indicates the total amount of + network information we have sent to the peer. */ + tr("Sent"), + /*: Title of Peers Table column which indicates the total amount of + network information we have received from the peer. */ + tr("Received"), + /*: Title of Peers Table column which contains the peer's + User Agent string. */ + tr("User Agent")}; std::unique_ptr<PeerTablePriv> priv; QTimer *timer; }; diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp index 1d0605c903..2257c2ad4f 100644 --- a/src/qt/platformstyle.cpp +++ b/src/qt/platformstyle.cpp @@ -71,25 +71,28 @@ PlatformStyle::PlatformStyle(const QString &_name, bool _imagesOnButtons, bool _ name(_name), imagesOnButtons(_imagesOnButtons), colorizeIcons(_colorizeIcons), - useExtraSpacing(_useExtraSpacing), - singleColor(0,0,0), - textColor(0,0,0) + useExtraSpacing(_useExtraSpacing) +{ +} + +QColor PlatformStyle::TextColor() const +{ + return QApplication::palette().color(QPalette::WindowText); +} + +QColor PlatformStyle::SingleColor() const { - // Determine icon highlighting color if (colorizeIcons) { const QColor colorHighlightBg(QApplication::palette().color(QPalette::Highlight)); const QColor colorHighlightFg(QApplication::palette().color(QPalette::HighlightedText)); const QColor colorText(QApplication::palette().color(QPalette::WindowText)); const int colorTextLightness = colorText.lightness(); - QColor colorbase; - if (abs(colorHighlightBg.lightness() - colorTextLightness) < abs(colorHighlightFg.lightness() - colorTextLightness)) - colorbase = colorHighlightBg; - else - colorbase = colorHighlightFg; - singleColor = colorbase; + if (abs(colorHighlightBg.lightness() - colorTextLightness) < abs(colorHighlightFg.lightness() - colorTextLightness)) { + return colorHighlightBg; + } + return colorHighlightFg; } - // Determine text color - textColor = QColor(QApplication::palette().color(QPalette::WindowText)); + return {0, 0, 0}; } QImage PlatformStyle::SingleColorImage(const QString& filename) const diff --git a/src/qt/platformstyle.h b/src/qt/platformstyle.h index 53632e56e2..9df0a1f985 100644 --- a/src/qt/platformstyle.h +++ b/src/qt/platformstyle.h @@ -21,8 +21,8 @@ public: bool getImagesOnButtons() const { return imagesOnButtons; } bool getUseExtraSpacing() const { return useExtraSpacing; } - QColor TextColor() const { return textColor; } - QColor SingleColor() const { return singleColor; } + QColor TextColor() const; + QColor SingleColor() const; /** Colorize an image (given filename) with the icon color */ QImage SingleColorImage(const QString& filename) const; @@ -43,9 +43,6 @@ private: bool imagesOnButtons; bool colorizeIcons; bool useExtraSpacing; - QColor singleColor; - QColor textColor; - /* ... more to come later */ }; #endif // BITCOIN_QT_PLATFORMSTYLE_H diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 17746b395b..99318c3bc0 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -141,7 +141,8 @@ void PSBTOperationsDialog::saveTransaction() { filename_suggestion.append(".psbt"); QString filename = GUIUtil::getSaveFileName(this, tr("Save Transaction Data"), filename_suggestion, - tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selected_filter); + //: Expanded name of the binary PSBT file format. See: BIP 174. + tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selected_filter); if (filename.isEmpty()) { return; } diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp index 3e1964915d..f5200bb5c0 100644 --- a/src/qt/qrimagewidget.cpp +++ b/src/qt/qrimagewidget.cpp @@ -118,7 +118,9 @@ void QRImageWidget::saveImage() return; QString fn = GUIUtil::getSaveFileName( this, tr("Save QR Code"), QString(), - tr("PNG Image", "Name of PNG file format") + QLatin1String(" (*.png)"), nullptr); + /*: Expanded name of the PNG file format. + See https://en.wikipedia.org/wiki/Portable_Network_Graphics */ + tr("PNG Image") + QLatin1String(" (*.png)"), nullptr); if (!fn.isEmpty()) { exportImage().save(fn); diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 03531a1381..1ecc2f67ef 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -14,6 +14,9 @@ #include <utility> +#include <QLatin1Char> +#include <QLatin1String> + RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) : QAbstractTableModel(parent), walletModel(parent) { @@ -124,7 +127,11 @@ void RecentRequestsTableModel::updateAmountColumnTitle() /** Gets title for amount column including current display unit if optionsModel reference available. */ QString RecentRequestsTableModel::getAmountTitle() { - return (this->walletModel->getOptionsModel() != nullptr) ? tr("Requested") + " ("+BitcoinUnits::shortName(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")" : ""; + if (!walletModel->getOptionsModel()) return {}; + return tr("Requested") + + QLatin1String(" (") + + BitcoinUnits::shortName(this->walletModel->getOptionsModel()->getDisplayUnit()) + + QLatin1Char(')'); } QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index afbdc07ba0..83a111e9c8 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -843,6 +843,25 @@ void RPCConsole::keyPressEvent(QKeyEvent *event) } } +void RPCConsole::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + ui->clearButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); + ui->fontBiggerButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/fontbigger"))); + ui->fontSmallerButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/fontsmaller"))); + ui->promptIcon->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/prompticon"))); + + for (int i = 0; ICON_MAPPING[i].url; ++i) { + ui->messagesWidget->document()->addResource( + QTextDocument::ImageResource, + QUrl(ICON_MAPPING[i].url), + platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(QSize(consoleFontSize * 2, consoleFontSize * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + } +#endif +} + void RPCConsole::message(int category, const QString &message, bool html) { QTime time = QTime::currentTime(); @@ -1049,7 +1068,7 @@ void RPCConsole::updateDetailWidget() const auto stats = selected_peers.first().data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>(); // update the detail ui with latest node information QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " "); - peerAddrDetails += tr("(peer id: %1)").arg(QString::number(stats->nodeStats.nodeid)); + peerAddrDetails += tr("(peer: %1)").arg(QString::number(stats->nodeStats.nodeid)); if (!stats->nodeStats.addrLocal.empty()) peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal)); ui->peerHeading->setText(peerAddrDetails); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 14d8900716..55a1decceb 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -74,6 +74,7 @@ public: protected: virtual bool eventFilter(QObject* obj, QEvent *event) override; void keyPressEvent(QKeyEvent *) override; + void changeEvent(QEvent* e) override; private Q_SLOTS: void on_lineEdit_returnPressed(); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index e3ea6e9015..160b43324f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -432,7 +432,8 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked) fileNameSuggestion.append(".psbt"); QString filename = GUIUtil::getSaveFileName(this, tr("Save Transaction Data"), fileNameSuggestion, - tr("Partially Signed Transaction (Binary)", "Name of binary PSBT file format") + QLatin1String(" (*.psbt)"), &selectedFilter); + //: Expanded name of the binary PSBT file format. See: BIP 174. + tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selectedFilter); if (filename.isEmpty()) { return; } diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 444dc79a2e..f701bb9615 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -236,6 +236,19 @@ void SendCoinsEntry::updateDisplayUnit() } } +void SendCoinsEntry::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book"))); + ui->pasteButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste"))); + ui->deleteButton->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); + ui->deleteButton_is->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); + ui->deleteButton_s->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); + } +#endif +} + bool SendCoinsEntry::updateLabel(const QString &address) { if(!model) diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 254cc186e2..e682e6423a 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -69,6 +69,9 @@ private Q_SLOTS: void on_pasteButton_clicked(); void updateDisplayUnit(); +protected: + void changeEvent(QEvent* e) override; + private: SendCoinsRecipient recipient; Ui::SendCoinsEntry *ui; diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 2e7df60574..b982cc577d 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -283,3 +283,19 @@ bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event) } return QDialog::eventFilter(object, event); } + +void SignVerifyMessageDialog::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + ui->addressBookButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book"))); + ui->pasteButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste"))); + ui->copySignatureButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editcopy"))); + ui->signMessageButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/edit"))); + ui->clearButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); + ui->addressBookButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book"))); + ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/transaction_0"))); + ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove"))); + } +#endif +} diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h index d98cb290a1..3d2d5f281e 100644 --- a/src/qt/signverifymessagedialog.h +++ b/src/qt/signverifymessagedialog.h @@ -31,6 +31,7 @@ public: protected: bool eventFilter(QObject *object, QEvent *event) override; + void changeEvent(QEvent* e) override; private: Ui::SignVerifyMessageDialog *ui; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index febfead6ad..26f568b16a 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -235,7 +235,7 @@ void TestGUI(interfaces::Node& node) QCOMPARE(uri.count("amount=0.00000001"), 2); QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:")); - QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString("0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); + QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString::fromStdString("0.00000001 " + CURRENCY_UNIT)); QCOMPARE(uri.count("label=TEST_LABEL_1"), 2); QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_tag")->text(), QString("Label:")); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index ece3a9cf48..02d220db20 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -26,6 +26,8 @@ #include <stdint.h> #include <string> +#include <QLatin1String> + QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks) { if (!status.is_final) @@ -38,14 +40,16 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const i else { int nDepth = status.depth_in_main_chain; - if (nDepth < 0) + if (nDepth < 0) { return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth); - else if (nDepth == 0) - return tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : ""); - else if (nDepth < 6) + } else if (nDepth == 0) { + const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()}; + return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned; + } else if (nDepth < 6) { return tr("%1/unconfirmed").arg(nDepth); - else + } else { return tr("%1 confirmations").arg(nDepth); + } } } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index a7556eed04..b68ceaedbb 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -25,6 +25,8 @@ #include <QDateTime> #include <QDebug> #include <QIcon> +#include <QLatin1Char> +#include <QLatin1String> #include <QList> @@ -96,18 +98,20 @@ public: */ QList<TransactionRecord> cachedWallet; - bool fQueueNotifications = false; + /** True when model finishes loading all wallet transactions on start */ + bool m_loaded = false; + /** True when transactions are being notified, for instance when scanning */ + bool m_loading = false; std::vector< TransactionNotification > vQueueNotifications; void NotifyTransactionChanged(const uint256 &hash, ChangeType status); - void ShowProgress(const std::string &title, int nProgress); + void DispatchNotifications(); /* Query entire wallet anew from core. */ void refreshWallet(interfaces::Wallet& wallet) { - qDebug() << "TransactionTablePriv::refreshWallet"; - cachedWallet.clear(); + assert(!m_loaded); { for (const auto& wtx : wallet.getWalletTxs()) { if (TransactionRecord::showTransaction()) { @@ -115,6 +119,8 @@ public: } } } + m_loaded = true; + DispatchNotifications(); } /* Update our model of the wallet incrementally, to synchronize our model of the wallet @@ -249,12 +255,12 @@ TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle fProcessingQueuedTransactions(false), platformStyle(_platformStyle) { + subscribeToCoreSignals(); + columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); priv->refreshWallet(walletModel->wallet()); connect(walletModel->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &TransactionTableModel::updateDisplayUnit); - - subscribeToCoreSignals(); } TransactionTableModel::~TransactionTableModel() @@ -409,9 +415,9 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const { QString watchAddress; - if (tooltip) { + if (tooltip && wtx->involvesWatchAddress) { // Mark transactions involving watch-only addresses by adding " (watch-only)" - watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : ""; + watchAddress = QLatin1String(" (") + tr("watch-only") + QLatin1Char(')'); } switch(wtx->type) @@ -719,7 +725,7 @@ void TransactionTablePriv::NotifyTransactionChanged(const uint256 &hash, ChangeT TransactionNotification notification(hash, status, showTransaction); - if (fQueueNotifications) + if (!m_loaded || m_loading) { vQueueNotifications.push_back(notification); return; @@ -727,36 +733,34 @@ void TransactionTablePriv::NotifyTransactionChanged(const uint256 &hash, ChangeT notification.invoke(parent); } -void TransactionTablePriv::ShowProgress(const std::string &title, int nProgress) +void TransactionTablePriv::DispatchNotifications() { - if (nProgress == 0) - fQueueNotifications = true; + if (!m_loaded || m_loading) return; - if (nProgress == 100) + if (vQueueNotifications.size() > 10) { // prevent balloon spam, show maximum 10 balloons + bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true)); + assert(invoked); + } + for (unsigned int i = 0; i < vQueueNotifications.size(); ++i) { - fQueueNotifications = false; - if (vQueueNotifications.size() > 10) { // prevent balloon spam, show maximum 10 balloons - bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true)); + if (vQueueNotifications.size() - i <= 10) { + bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false)); assert(invoked); } - for (unsigned int i = 0; i < vQueueNotifications.size(); ++i) - { - if (vQueueNotifications.size() - i <= 10) { - bool invoked = QMetaObject::invokeMethod(parent, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false)); - assert(invoked); - } - vQueueNotifications[i].invoke(parent); - } - vQueueNotifications.clear(); + vQueueNotifications[i].invoke(parent); } + vQueueNotifications.clear(); } void TransactionTableModel::subscribeToCoreSignals() { // Connect signals to wallet m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(std::bind(&TransactionTablePriv::NotifyTransactionChanged, priv, std::placeholders::_1, std::placeholders::_2)); - m_handler_show_progress = walletModel->wallet().handleShowProgress(std::bind(&TransactionTablePriv::ShowProgress, priv, std::placeholders::_1, std::placeholders::_2)); + m_handler_show_progress = walletModel->wallet().handleShowProgress([this](const std::string&, int progress) { + priv->m_loading = progress < 100; + priv->DispatchNotifications(); + }); } void TransactionTableModel::unsubscribeFromCoreSignals() diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 7a975dadae..1e8e012dcf 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -37,8 +37,8 @@ #include <QUrl> #include <QVBoxLayout> -TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) : - QWidget(parent) +TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) + : QWidget(parent), m_platform_style{platformStyle} { // Build filter row setContentsMargins(0,0,0,0); @@ -243,6 +243,20 @@ void TransactionView::setModel(WalletModel *_model) } } +void TransactionView::changeEvent(QEvent* e) +{ +#ifdef Q_OS_MACOS + if (e->type() == QEvent::PaletteChange) { + watchOnlyWidget->setItemIcon( + TransactionFilterProxy::WatchOnlyFilter_Yes, + m_platform_style->SingleColorIcon(QStringLiteral(":/icons/eye_plus"))); + watchOnlyWidget->setItemIcon( + TransactionFilterProxy::WatchOnlyFilter_No, + m_platform_style->SingleColorIcon(QStringLiteral(":/icons/eye_minus"))); + } +#endif +} + void TransactionView::chooseDate(int idx) { if (!transactionProxyModel) return; @@ -336,7 +350,9 @@ void TransactionView::exportClicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName(this, tr("Export Transaction History"), QString(), - tr("Comma separated file", "Name of CSV file format") + QLatin1String(" (*.csv)"), nullptr); + /*: Expanded name of the CSV file format. + See https://en.wikipedia.org/wiki/Comma-separated_values */ + tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr); if (filename.isNull()) return; diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 66350bdc02..3e2321d91b 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -60,6 +60,9 @@ public: MINIMUM_COLUMN_WIDTH = 23 }; +protected: + void changeEvent(QEvent* e) override; + private: WalletModel *model{nullptr}; TransactionFilterProxy *transactionProxyModel{nullptr}; @@ -85,6 +88,8 @@ private: bool eventFilter(QObject *obj, QEvent *event) override; + const PlatformStyle* m_platform_style; + private Q_SLOTS: void contextualMenu(const QPoint &); void dateRangeChanged(); diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index c152689f0b..aa26a01541 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -207,6 +207,9 @@ void WalletControllerActivity::showProgressDialog(const QString& label_text) m_progress_dialog->setCancelButton(nullptr); m_progress_dialog->setWindowModality(Qt::ApplicationModal); GUIUtil::PolishProgressDialog(m_progress_dialog); + // The setValue call forces QProgressDialog to start the internal duration estimation. + // See details in https://bugreports.qt.io/browse/QTBUG-47042. + m_progress_dialog->setValue(0); } void WalletControllerActivity::destroyProgressDialog() diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 67cc42725b..cc9e1502f0 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -273,7 +273,8 @@ void WalletView::backupWallet() { QString filename = GUIUtil::getSaveFileName(this, tr("Backup Wallet"), QString(), - tr("Wallet Data", "Name of wallet data file format") + QLatin1String(" (*.dat)"), nullptr); + //: Name of the wallet data file format. + tr("Wallet Data") + QLatin1String(" (*.dat)"), nullptr); if (filename.isEmpty()) return; @@ -330,7 +331,6 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog = new QProgressDialog(title, tr("Cancel"), 0, 100); GUIUtil::PolishProgressDialog(progressDialog); progressDialog->setWindowModality(Qt::ApplicationModal); - progressDialog->setMinimumDuration(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); } else if (nProgress == 100) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 4999eefc24..c4d6e3d546 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -931,26 +931,22 @@ static RPCHelpMan addpeeraddress() throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled"); } - UniValue obj(UniValue::VOBJ); - - std::string addr_string = request.params[0].get_str(); - uint16_t port{static_cast<uint16_t>(request.params[1].get_int())}; + const std::string& addr_string{request.params[0].get_str()}; + const uint16_t port{static_cast<uint16_t>(request.params[1].get_int())}; + UniValue obj(UniValue::VOBJ); CNetAddr net_addr; - if (!LookupHost(addr_string, net_addr, false)) { - obj.pushKV("success", false); - return obj; - } - CAddress address = CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK|NODE_WITNESS)); - address.nTime = GetAdjustedTime(); - // The source address is set equal to the address. This is equivalent to the peer - // announcing itself. - if (!node.addrman->Add(address, address)) { - obj.pushKV("success", false); - return obj; + bool success{false}; + + if (LookupHost(addr_string, net_addr, false)) { + CAddress address{CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK | NODE_WITNESS))}; + address.nTime = GetAdjustedTime(); + // The source address is set equal to the address. This is equivalent to the peer + // announcing itself. + if (node.addrman->Add(address, address)) success = true; } - obj.pushKV("success", true); + obj.pushKV("success", success); return obj; }, }; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index adb8ac0595..339d711ac9 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -15,6 +15,7 @@ #include <node/context.h> #include <node/psbt.h> #include <node/transaction.h> +#include <policy/packages.h> #include <policy/policy.h> #include <policy/rbf.h> #include <primitives/transaction.h> @@ -885,8 +886,11 @@ static RPCHelpMan sendrawtransaction() static RPCHelpMan testmempoolaccept() { return RPCHelpMan{"testmempoolaccept", - "\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n" - "\nThis checks if the transaction violates the consensus or policy rules.\n" + "\nReturns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n" + "\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n" + "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n" + "\nThe maximum number of transactions allowed is 25 (MAX_PACKAGE_COUNT)\n" + "\nThis checks if transactions violate the consensus or policy rules.\n" "\nSee sendrawtransaction call.\n", { {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.\n" @@ -895,17 +899,21 @@ static RPCHelpMan testmempoolaccept() {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, }, }, - {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())}, "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kvB\n"}, + {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())}, + "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kvB\n"}, }, RPCResult{ RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n" - "Length is exactly one for now.", + "Returns results for each transaction in the same order they were passed in.\n" + "It is possible for transactions to not be fully validated ('allowed' unset) if an earlier transaction failed.\n", { {RPCResult::Type::OBJ, "", "", { {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"}, - {RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"}, + {RPCResult::Type::STR, "package-error", "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."}, + {RPCResult::Type::BOOL, "allowed", "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate." + "If not present, the tx was not fully validated due to a failure in another tx in the list."}, {RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"}, {RPCResult::Type::OBJ, "fees", "Transaction fees (only present if 'allowed' is true)", { @@ -932,62 +940,86 @@ static RPCHelpMan testmempoolaccept() UniValueType(), // VNUM or VSTR, checked inside AmountFromValue() }); - if (request.params[0].get_array().size() != 1) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now"); - } - - CMutableTransaction mtx; - if (!DecodeHexTx(mtx, request.params[0].get_array()[0].get_str())) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input."); + const UniValue raw_transactions = request.params[0].get_array(); + if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions."); } - CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ? DEFAULT_MAX_RAW_TX_FEE_RATE : CFeeRate(AmountFromValue(request.params[1])); - NodeContext& node = EnsureAnyNodeContext(request.context); + std::vector<CTransactionRef> txns; + for (const auto& rawtx : raw_transactions.getValues()) { + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, rawtx.get_str())) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, + "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input."); + } + txns.emplace_back(MakeTransactionRef(std::move(mtx))); + } + NodeContext& node = EnsureAnyNodeContext(request.context); CTxMemPool& mempool = EnsureMemPool(node); - int64_t virtual_size = GetVirtualTransactionSize(*tx); - CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); - - UniValue result(UniValue::VARR); - UniValue result_0(UniValue::VOBJ); - result_0.pushKV("txid", tx->GetHash().GetHex()); - result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex()); - - ChainstateManager& chainman = EnsureChainman(node); - const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(chainman.ActiveChainstate(), mempool, std::move(tx), - false /* bypass_limits */, /* test_accept */ true)); - - // Only return the fee and vsize if the transaction would pass ATMP. - // These can be used to calculate the feerate. - if (accept_result.m_result_type == MempoolAcceptResult::ResultType::VALID) { - const CAmount fee = accept_result.m_base_fees.value(); - // Check that fee does not exceed maximum fee - if (max_raw_tx_fee && fee > max_raw_tx_fee) { - result_0.pushKV("allowed", false); - result_0.pushKV("reject-reason", "max-fee-exceeded"); - } else { - result_0.pushKV("allowed", true); - result_0.pushKV("vsize", virtual_size); - UniValue fees(UniValue::VOBJ); - fees.pushKV("base", ValueFromAmount(fee)); - result_0.pushKV("fees", fees); + CChainState& chainstate = EnsureChainman(node).ActiveChainstate(); + const PackageMempoolAcceptResult package_result = [&] { + LOCK(::cs_main); + if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true); + return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(), + AcceptToMemoryPool(chainstate, mempool, txns[0], /* bypass_limits */ false, /* test_accept*/ true)); + }(); + + UniValue rpc_result(UniValue::VARR); + // We will check transaction fees we iterate through txns in order. If any transaction fee + // exceeds maxfeerate, we will keave the rest of the validation results blank, because it + // doesn't make sense to return a validation result for a transaction if its ancestor(s) would + // not be submitted. + bool exit_early{false}; + for (const auto& tx : txns) { + UniValue result_inner(UniValue::VOBJ); + result_inner.pushKV("txid", tx->GetHash().GetHex()); + result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex()); + if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) { + result_inner.pushKV("package-error", package_result.m_state.GetRejectReason()); } - result.push_back(std::move(result_0)); - } else { - result_0.pushKV("allowed", false); - const TxValidationState state = accept_result.m_state; - if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { - result_0.pushKV("reject-reason", "missing-inputs"); + auto it = package_result.m_tx_results.find(tx->GetWitnessHash()); + if (exit_early || it == package_result.m_tx_results.end()) { + // Validation unfinished. Just return the txid and wtxid. + rpc_result.push_back(result_inner); + continue; + } + const auto& tx_result = it->second; + if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) { + const CAmount fee = tx_result.m_base_fees.value(); + // Check that fee does not exceed maximum fee + const int64_t virtual_size = GetVirtualTransactionSize(*tx); + const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); + if (max_raw_tx_fee && fee > max_raw_tx_fee) { + result_inner.pushKV("allowed", false); + result_inner.pushKV("reject-reason", "max-fee-exceeded"); + exit_early = true; + } else { + // Only return the fee and vsize if the transaction would pass ATMP. + // These can be used to calculate the feerate. + result_inner.pushKV("allowed", true); + result_inner.pushKV("vsize", virtual_size); + UniValue fees(UniValue::VOBJ); + fees.pushKV("base", ValueFromAmount(fee)); + result_inner.pushKV("fees", fees); + } } else { - result_0.pushKV("reject-reason", state.GetRejectReason()); + result_inner.pushKV("allowed", false); + const TxValidationState state = tx_result.m_state; + if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { + result_inner.pushKV("reject-reason", "missing-inputs"); + } else { + result_inner.pushKV("reject-reason", state.GetRejectReason()); + } } - result.push_back(std::move(result_0)); + rpc_result.push_back(result_inner); } - return result; + return rpc_result; }, }; } diff --git a/src/serialize.h b/src/serialize.h index d9ca984f9c..5ef846b9e9 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -122,34 +122,6 @@ template<typename Stream> inline uint64_t ser_readdata64(Stream &s) s.read((char*)&obj, 8); return le64toh(obj); } -inline uint64_t ser_double_to_uint64(double x) -{ - uint64_t tmp; - std::memcpy(&tmp, &x, sizeof(x)); - static_assert(sizeof(tmp) == sizeof(x), "double and uint64_t assumed to have the same size"); - return tmp; -} -inline uint32_t ser_float_to_uint32(float x) -{ - uint32_t tmp; - std::memcpy(&tmp, &x, sizeof(x)); - static_assert(sizeof(tmp) == sizeof(x), "float and uint32_t assumed to have the same size"); - return tmp; -} -inline double ser_uint64_to_double(uint64_t y) -{ - double tmp; - std::memcpy(&tmp, &y, sizeof(y)); - static_assert(sizeof(tmp) == sizeof(y), "double and uint64_t assumed to have the same size"); - return tmp; -} -inline float ser_uint32_to_float(uint32_t y) -{ - float tmp; - std::memcpy(&tmp, &y, sizeof(y)); - static_assert(sizeof(tmp) == sizeof(y), "float and uint32_t assumed to have the same size"); - return tmp; -} ///////////////////////////////////////////////////////////////// @@ -234,8 +206,6 @@ template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_wri template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); } template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); } template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); } -template<typename Stream> inline void Serialize(Stream& s, float a ) { ser_writedata32(s, ser_float_to_uint32(a)); } -template<typename Stream> inline void Serialize(Stream& s, double a ) { ser_writedata64(s, ser_double_to_uint64(a)); } template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); } template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); } template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); } @@ -252,8 +222,6 @@ template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a = template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); } template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); } template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); } -template<typename Stream> inline void Unserialize(Stream& s, float& a ) { a = ser_uint32_to_float(ser_readdata32(s)); } -template<typename Stream> inline void Unserialize(Stream& s, double& a ) { a = ser_uint64_to_double(ser_readdata64(s)); } template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); } template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); } template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(CharCast(span.data()), span.size()); } diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp index d18a87d177..adef66a3ee 100644 --- a/src/test/fuzz/float.cpp +++ b/src/test/fuzz/float.cpp @@ -3,14 +3,14 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <memusage.h> -#include <serialize.h> -#include <streams.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> +#include <util/serfloat.h> #include <version.h> #include <cassert> -#include <cstdint> +#include <cmath> +#include <limits> FUZZ_TARGET(float) { @@ -19,24 +19,17 @@ FUZZ_TARGET(float) { const double d = fuzzed_data_provider.ConsumeFloatingPoint<double>(); (void)memusage::DynamicUsage(d); - assert(ser_uint64_to_double(ser_double_to_uint64(d)) == d); - CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION); - stream << d; - double d_deserialized; - stream >> d_deserialized; - assert(d == d_deserialized); - } - - { - const float f = fuzzed_data_provider.ConsumeFloatingPoint<float>(); - (void)memusage::DynamicUsage(f); - assert(ser_uint32_to_float(ser_float_to_uint32(f)) == f); - - CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION); - stream << f; - float f_deserialized; - stream >> f_deserialized; - assert(f == f_deserialized); + uint64_t encoded = EncodeDouble(d); + if constexpr (std::numeric_limits<double>::is_iec559) { + if (!std::isnan(d)) { + uint64_t encoded_in_memory; + std::copy((const unsigned char*)&d, (const unsigned char*)(&d + 1), (unsigned char*)&encoded_in_memory); + assert(encoded_in_memory == encoded); + } + } + double d_deserialized = DecodeDouble(encoded); + assert(std::isnan(d) == std::isnan(d_deserialized)); + assert(std::isnan(d) || d == d_deserialized); } } diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp deleted file mode 100644 index 3a1fdaad8f..0000000000 --- a/src/test/fuzz/p2p_transport_deserializer.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2019-2020 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include <chainparams.h> -#include <net.h> -#include <protocol.h> -#include <test/fuzz/fuzz.h> - -#include <cassert> -#include <cstdint> -#include <limits> -#include <optional> -#include <vector> - -void initialize_p2p_transport_deserializer() -{ - SelectParams(CBaseChainParams::REGTEST); -} - -FUZZ_TARGET_INIT(p2p_transport_deserializer, initialize_p2p_transport_deserializer) -{ - // Construct deserializer, with a dummy NodeId - V1TransportDeserializer deserializer{Params(), (NodeId)0, SER_NETWORK, INIT_PROTO_VERSION}; - Span<const uint8_t> msg_bytes{buffer}; - while (msg_bytes.size() > 0) { - const int handled = deserializer.Read(msg_bytes); - if (handled < 0) { - break; - } - if (deserializer.Complete()) { - const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()}; - uint32_t out_err_raw_size{0}; - std::optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)}; - if (result) { - assert(result->m_command.size() <= CMessageHeader::COMMAND_SIZE); - assert(result->m_raw_message_size <= buffer.size()); - assert(result->m_raw_message_size == CMessageHeader::HEADER_SIZE + result->m_message_size); - assert(result->m_time == m_time); - } - } - } -} diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp new file mode 100644 index 0000000000..edee5aeef7 --- /dev/null +++ b/src/test/fuzz/p2p_transport_serialization.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2019-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <chainparams.h> +#include <hash.h> +#include <net.h> +#include <netmessagemaker.h> +#include <protocol.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> + +#include <cassert> +#include <cstdint> +#include <limits> +#include <optional> +#include <vector> + +void initialize_p2p_transport_serialization() +{ + SelectParams(CBaseChainParams::REGTEST); +} + +FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serialization) +{ + // Construct deserializer, with a dummy NodeId + V1TransportDeserializer deserializer{Params(), (NodeId)0, SER_NETWORK, INIT_PROTO_VERSION}; + V1TransportSerializer serializer{}; + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + + auto checksum_assist = fuzzed_data_provider.ConsumeBool(); + auto magic_bytes_assist = fuzzed_data_provider.ConsumeBool(); + std::vector<uint8_t> mutable_msg_bytes; + + auto header_bytes_remaining = CMessageHeader::HEADER_SIZE; + if (magic_bytes_assist) { + auto msg_start = Params().MessageStart(); + for (size_t i = 0; i < CMessageHeader::MESSAGE_SIZE_SIZE; ++i) { + mutable_msg_bytes.push_back(msg_start[i]); + } + header_bytes_remaining -= CMessageHeader::MESSAGE_SIZE_SIZE; + } + + if (checksum_assist) { + header_bytes_remaining -= CMessageHeader::CHECKSUM_SIZE; + } + + auto header_random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(header_bytes_remaining); + mutable_msg_bytes.insert(mutable_msg_bytes.end(), header_random_bytes.begin(), header_random_bytes.end()); + auto payload_bytes = fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>(); + + if (checksum_assist && mutable_msg_bytes.size() == CMessageHeader::CHECKSUM_OFFSET) { + CHash256 hasher; + unsigned char hsh[32]; + hasher.Write(payload_bytes); + hasher.Finalize(hsh); + for (size_t i = 0; i < CMessageHeader::CHECKSUM_SIZE; ++i) { + mutable_msg_bytes.push_back(hsh[i]); + } + } + + mutable_msg_bytes.insert(mutable_msg_bytes.end(), payload_bytes.begin(), payload_bytes.end()); + Span<const uint8_t> msg_bytes{mutable_msg_bytes}; + while (msg_bytes.size() > 0) { + const int handled = deserializer.Read(msg_bytes); + if (handled < 0) { + break; + } + if (deserializer.Complete()) { + const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()}; + uint32_t out_err_raw_size{0}; + std::optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)}; + if (result) { + assert(result->m_command.size() <= CMessageHeader::COMMAND_SIZE); + assert(result->m_raw_message_size <= mutable_msg_bytes.size()); + assert(result->m_raw_message_size == CMessageHeader::HEADER_SIZE + result->m_message_size); + assert(result->m_time == m_time); + + std::vector<unsigned char> header; + auto msg = CNetMsgMaker{result->m_recv.GetVersion()}.Make(result->m_command, MakeUCharSpan(result->m_recv)); + serializer.prepareForTransport(msg, header); + } + } + } +} diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index ad11f2c5f2..bab34ea340 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -219,6 +219,16 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool) RegisterSharedValidationInterface(txr); const bool bypass_limits = fuzzed_data_provider.ConsumeBool(); ::fRequireStandard = fuzzed_data_provider.ConsumeBool(); + + // Make sure ProcessNewPackage on one transaction works and always fully validates the transaction. + // The result is not guaranteed to be the same as what is returned by ATMP. + const auto result_package = WITH_LOCK(::cs_main, + return ProcessNewPackage(node.chainman->ActiveChainstate(), tx_pool, {tx}, true)); + auto it = result_package.m_tx_results.find(tx->GetWitnessHash()); + Assert(it != result_package.m_tx_results.end()); + Assert(it->second.m_result_type == MempoolAcceptResult::ResultType::VALID || + it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID); + const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx_pool, tx, bypass_limits)); const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; SyncWithValidationInterfaceQueue(); diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index 48b7877896..36b1d5035c 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -384,7 +384,7 @@ public: [&] { mode = "a+"; }); -#ifdef _GNU_SOURCE +#if defined _GNU_SOURCE && !defined __ANDROID__ const cookie_io_functions_t io_hooks = { FuzzedFileProvider::read, FuzzedFileProvider::write, @@ -513,8 +513,6 @@ void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noe WRITE_TO_STREAM_CASE(uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()), WRITE_TO_STREAM_CASE(int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()), WRITE_TO_STREAM_CASE(uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()), - WRITE_TO_STREAM_CASE(float, fuzzed_data_provider.ConsumeFloatingPoint<float>()), - WRITE_TO_STREAM_CASE(double, fuzzed_data_provider.ConsumeFloatingPoint<double>()), WRITE_TO_STREAM_CASE(std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)), WRITE_TO_STREAM_CASE(std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider))); } catch (const std::ios_base::failure&) { @@ -545,8 +543,6 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no READ_FROM_STREAM_CASE(uint32_t), READ_FROM_STREAM_CASE(int64_t), READ_FROM_STREAM_CASE(uint64_t), - READ_FROM_STREAM_CASE(float), - READ_FROM_STREAM_CASE(double), READ_FROM_STREAM_CASE(std::string), READ_FROM_STREAM_CASE(std::vector<char>)); } catch (const std::ios_base::failure&) { diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 9ba004cc38..c47d0eae1e 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -28,7 +28,8 @@ struct MinerTestingSetup : public TestingSetup { void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs); bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs) { - return CheckSequenceLocks(::ChainstateActive(), *m_node.mempool, tx, flags); + CCoinsViewMemPool viewMempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool); + return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), viewMempool, tx, flags); } BlockAssembler AssemblerForTest(const CChainParams& params); }; diff --git a/src/test/serfloat_tests.cpp b/src/test/serfloat_tests.cpp new file mode 100644 index 0000000000..54e07b0f61 --- /dev/null +++ b/src/test/serfloat_tests.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2014-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <hash.h> +#include <test/util/setup_common.h> +#include <util/serfloat.h> +#include <serialize.h> +#include <streams.h> + +#include <boost/test/unit_test.hpp> + +#include <cmath> +#include <limits> + +BOOST_FIXTURE_TEST_SUITE(serfloat_tests, BasicTestingSetup) + +namespace { + +uint64_t TestDouble(double f) { + uint64_t i = EncodeDouble(f); + double f2 = DecodeDouble(i); + if (std::isnan(f)) { + // NaN is not guaranteed to round-trip exactly. + BOOST_CHECK(std::isnan(f2)); + } else { + // Everything else is. + BOOST_CHECK(!std::isnan(f2)); + uint64_t i2 = EncodeDouble(f2); + BOOST_CHECK_EQUAL(f, f2); + BOOST_CHECK_EQUAL(i, i2); + } + return i; +} + +} // namespace + +BOOST_AUTO_TEST_CASE(double_serfloat_tests) { + BOOST_CHECK_EQUAL(TestDouble(0.0), 0); + BOOST_CHECK_EQUAL(TestDouble(-0.0), 0x8000000000000000); + BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::infinity()), 0x7ff0000000000000); + BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::infinity()), 0xfff0000000000000); + BOOST_CHECK_EQUAL(TestDouble(0.5), 0x3fe0000000000000ULL); + BOOST_CHECK_EQUAL(TestDouble(1.0), 0x3ff0000000000000ULL); + BOOST_CHECK_EQUAL(TestDouble(2.0), 0x4000000000000000ULL); + BOOST_CHECK_EQUAL(TestDouble(4.0), 0x4010000000000000ULL); + BOOST_CHECK_EQUAL(TestDouble(785.066650390625), 0x4088888880000000ULL); + + // Roundtrip test on IEC559-compatible systems + if (std::numeric_limits<double>::is_iec559) { + BOOST_CHECK_EQUAL(sizeof(double), 8); + BOOST_CHECK_EQUAL(sizeof(uint64_t), 8); + // Test extreme values + TestDouble(std::numeric_limits<double>::min()); + TestDouble(-std::numeric_limits<double>::min()); + TestDouble(std::numeric_limits<double>::max()); + TestDouble(-std::numeric_limits<double>::max()); + TestDouble(std::numeric_limits<double>::lowest()); + TestDouble(-std::numeric_limits<double>::lowest()); + TestDouble(std::numeric_limits<double>::quiet_NaN()); + TestDouble(-std::numeric_limits<double>::quiet_NaN()); + TestDouble(std::numeric_limits<double>::signaling_NaN()); + TestDouble(-std::numeric_limits<double>::signaling_NaN()); + TestDouble(std::numeric_limits<double>::denorm_min()); + TestDouble(-std::numeric_limits<double>::denorm_min()); + // Test exact encoding: on currently supported platforms, EncodeDouble + // should produce exactly the same as the in-memory representation for non-NaN. + for (int j = 0; j < 1000; ++j) { + // Iterate over 9 specific bits exhaustively; the others are chosen randomly. + // These specific bits are the sign bit, and the 2 top and bottom bits of + // exponent and mantissa in the IEEE754 binary64 format. + for (int x = 0; x < 512; ++x) { + uint64_t v = InsecureRandBits(64); + v &= ~(uint64_t{1} << 0); + if (x & 1) v |= (uint64_t{1} << 0); + v &= ~(uint64_t{1} << 1); + if (x & 2) v |= (uint64_t{1} << 1); + v &= ~(uint64_t{1} << 50); + if (x & 4) v |= (uint64_t{1} << 50); + v &= ~(uint64_t{1} << 51); + if (x & 8) v |= (uint64_t{1} << 51); + v &= ~(uint64_t{1} << 52); + if (x & 16) v |= (uint64_t{1} << 52); + v &= ~(uint64_t{1} << 53); + if (x & 32) v |= (uint64_t{1} << 53); + v &= ~(uint64_t{1} << 61); + if (x & 64) v |= (uint64_t{1} << 61); + v &= ~(uint64_t{1} << 62); + if (x & 128) v |= (uint64_t{1} << 62); + v &= ~(uint64_t{1} << 63); + if (x & 256) v |= (uint64_t{1} << 63); + double f; + memcpy(&f, &v, 8); + uint64_t v2 = TestDouble(f); + if (!std::isnan(f)) BOOST_CHECK_EQUAL(v, v2); + } + } + } +} + +/* +Python code to generate the below hashes: + + def reversed_hex(x): + return binascii.hexlify(''.join(reversed(x))) + def dsha256(x): + return hashlib.sha256(hashlib.sha256(x).digest()).digest() + + reversed_hex(dsha256(''.join(struct.pack('<d', x) for x in range(0,1000)))) == '43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96' +*/ +BOOST_AUTO_TEST_CASE(doubles) +{ + CDataStream ss(SER_DISK, 0); + // encode + for (int i = 0; i < 1000; i++) { + ss << EncodeDouble(i); + } + BOOST_CHECK(Hash(ss) == uint256S("43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96")); + + // decode + for (int i = 0; i < 1000; i++) { + uint64_t val; + ss >> val; + double j = DecodeDouble(val); + BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index f77cda7ba2..58709178a4 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -70,8 +70,6 @@ BOOST_AUTO_TEST_CASE(sizes) BOOST_CHECK_EQUAL(sizeof(uint32_t), GetSerializeSize(uint32_t(0), 0)); BOOST_CHECK_EQUAL(sizeof(int64_t), GetSerializeSize(int64_t(0), 0)); BOOST_CHECK_EQUAL(sizeof(uint64_t), GetSerializeSize(uint64_t(0), 0)); - BOOST_CHECK_EQUAL(sizeof(float), GetSerializeSize(float(0), 0)); - BOOST_CHECK_EQUAL(sizeof(double), GetSerializeSize(double(0), 0)); // Bool is serialized as char BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(bool(0), 0)); @@ -85,93 +83,9 @@ BOOST_AUTO_TEST_CASE(sizes) BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0), 0), 4U); BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0), 0), 8U); BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0), 0), 8U); - BOOST_CHECK_EQUAL(GetSerializeSize(float(0), 0), 4U); - BOOST_CHECK_EQUAL(GetSerializeSize(double(0), 0), 8U); BOOST_CHECK_EQUAL(GetSerializeSize(bool(0), 0), 1U); } -BOOST_AUTO_TEST_CASE(floats_conversion) -{ - // Choose values that map unambiguously to binary floating point to avoid - // rounding issues at the compiler side. - BOOST_CHECK_EQUAL(ser_uint32_to_float(0x00000000), 0.0F); - BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f000000), 0.5F); - BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f800000), 1.0F); - BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40000000), 2.0F); - BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40800000), 4.0F); - BOOST_CHECK_EQUAL(ser_uint32_to_float(0x44444444), 785.066650390625F); - - BOOST_CHECK_EQUAL(ser_float_to_uint32(0.0F), 0x00000000U); - BOOST_CHECK_EQUAL(ser_float_to_uint32(0.5F), 0x3f000000U); - BOOST_CHECK_EQUAL(ser_float_to_uint32(1.0F), 0x3f800000U); - BOOST_CHECK_EQUAL(ser_float_to_uint32(2.0F), 0x40000000U); - BOOST_CHECK_EQUAL(ser_float_to_uint32(4.0F), 0x40800000U); - BOOST_CHECK_EQUAL(ser_float_to_uint32(785.066650390625F), 0x44444444U); -} - -BOOST_AUTO_TEST_CASE(doubles_conversion) -{ - // Choose values that map unambiguously to binary floating point to avoid - // rounding issues at the compiler side. - BOOST_CHECK_EQUAL(ser_uint64_to_double(0x0000000000000000ULL), 0.0); - BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3fe0000000000000ULL), 0.5); - BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3ff0000000000000ULL), 1.0); - BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4000000000000000ULL), 2.0); - BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4010000000000000ULL), 4.0); - BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4088888880000000ULL), 785.066650390625); - - BOOST_CHECK_EQUAL(ser_double_to_uint64(0.0), 0x0000000000000000ULL); - BOOST_CHECK_EQUAL(ser_double_to_uint64(0.5), 0x3fe0000000000000ULL); - BOOST_CHECK_EQUAL(ser_double_to_uint64(1.0), 0x3ff0000000000000ULL); - BOOST_CHECK_EQUAL(ser_double_to_uint64(2.0), 0x4000000000000000ULL); - BOOST_CHECK_EQUAL(ser_double_to_uint64(4.0), 0x4010000000000000ULL); - BOOST_CHECK_EQUAL(ser_double_to_uint64(785.066650390625), 0x4088888880000000ULL); -} -/* -Python code to generate the below hashes: - - def reversed_hex(x): - return binascii.hexlify(''.join(reversed(x))) - def dsha256(x): - return hashlib.sha256(hashlib.sha256(x).digest()).digest() - - reversed_hex(dsha256(''.join(struct.pack('<f', x) for x in range(0,1000)))) == '8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85099d7f3fe0c' - reversed_hex(dsha256(''.join(struct.pack('<d', x) for x in range(0,1000)))) == '43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96' -*/ -BOOST_AUTO_TEST_CASE(floats) -{ - CDataStream ss(SER_DISK, 0); - // encode - for (int i = 0; i < 1000; i++) { - ss << float(i); - } - BOOST_CHECK(Hash(ss) == uint256S("8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85099d7f3fe0c")); - - // decode - for (int i = 0; i < 1000; i++) { - float j; - ss >> j; - BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); - } -} - -BOOST_AUTO_TEST_CASE(doubles) -{ - CDataStream ss(SER_DISK, 0); - // encode - for (int i = 0; i < 1000; i++) { - ss << double(i); - } - BOOST_CHECK(Hash(ss) == uint256S("43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96")); - - // decode - for (int i = 0; i < 1000; i++) { - double j; - ss >> j; - BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); - } -} - BOOST_AUTO_TEST_CASE(varints) { // encode diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp index 8d14071297..95ad85d0f8 100644 --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -3,8 +3,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <consensus/validation.h> +#include <key_io.h> +#include <policy/packages.h> +#include <policy/policy.h> #include <primitives/transaction.h> #include <script/script.h> +#include <script/standard.h> #include <test/util/setup_common.h> #include <validation.h> @@ -47,4 +51,98 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS); } +// Create placeholder transactions that have no meaning. +inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs) +{ + CMutableTransaction mtx = CMutableTransaction(); + mtx.vin.resize(num_inputs); + mtx.vout.resize(num_outputs); + auto random_script = CScript() << ToByteVector(InsecureRand256()) << ToByteVector(InsecureRand256()); + for (size_t i{0}; i < num_inputs; ++i) { + mtx.vin[i].prevout.hash = InsecureRand256(); + mtx.vin[i].prevout.n = 0; + mtx.vin[i].scriptSig = random_script; + } + for (size_t o{0}; o < num_outputs; ++o) { + mtx.vout[o].nValue = 1 * CENT; + mtx.vout[o].scriptPubKey = random_script; + } + return MakeTransactionRef(mtx); +} + +BOOST_FIXTURE_TEST_CASE(package_tests, TestChain100Setup) +{ + LOCK(cs_main); + unsigned int initialPoolSize = m_node.mempool->size(); + + // Parent and Child Package + CKey parent_key; + parent_key.MakeNewKey(true); + CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey())); + auto mtx_parent = CreateValidMempoolTransaction(/* input_transaction */ m_coinbase_txns[0], /* vout */ 0, + /* input_height */ 0, /* input_signing_key */ coinbaseKey, + /* output_destination */ parent_locking_script, + /* output_amount */ CAmount(49 * COIN), /* submit */ false); + CTransactionRef tx_parent = MakeTransactionRef(mtx_parent); + + CKey child_key; + child_key.MakeNewKey(true); + CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey())); + auto mtx_child = CreateValidMempoolTransaction(/* input_transaction */ tx_parent, /* vout */ 0, + /* input_height */ 101, /* input_signing_key */ parent_key, + /* output_destination */ child_locking_script, + /* output_amount */ CAmount(48 * COIN), /* submit */ false); + CTransactionRef tx_child = MakeTransactionRef(mtx_child); + const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {tx_parent, tx_child}, /* test_accept */ true); + BOOST_CHECK_MESSAGE(result_parent_child.m_state.IsValid(), + "Package validation unexpectedly failed: " << result_parent_child.m_state.GetRejectReason()); + auto it_parent = result_parent_child.m_tx_results.find(tx_parent->GetWitnessHash()); + auto it_child = result_parent_child.m_tx_results.find(tx_child->GetWitnessHash()); + BOOST_CHECK(it_parent != result_parent_child.m_tx_results.end()); + BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(), + "Package validation unexpectedly failed: " << it_parent->second.m_state.GetRejectReason()); + BOOST_CHECK(it_child != result_parent_child.m_tx_results.end()); + BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(), + "Package validation unexpectedly failed: " << it_child->second.m_state.GetRejectReason()); + + // Packages can't have more than 25 transactions. + Package package_too_many; + package_too_many.reserve(MAX_PACKAGE_COUNT + 1); + for (size_t i{0}; i < MAX_PACKAGE_COUNT + 1; ++i) { + package_too_many.emplace_back(create_placeholder_tx(1, 1)); + } + auto result_too_many = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_many, /* test_accept */ true); + BOOST_CHECK(result_too_many.m_state.IsInvalid()); + BOOST_CHECK_EQUAL(result_too_many.m_state.GetResult(), PackageValidationResult::PCKG_POLICY); + BOOST_CHECK_EQUAL(result_too_many.m_state.GetRejectReason(), "package-too-many-transactions"); + + // Packages can't have a total size of more than 101KvB. + CTransactionRef large_ptx = create_placeholder_tx(150, 150); + Package package_too_large; + auto size_large = GetVirtualTransactionSize(*large_ptx); + size_t total_size{0}; + while (total_size <= MAX_PACKAGE_SIZE * 1000) { + package_too_large.push_back(large_ptx); + total_size += size_large; + } + BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT); + auto result_too_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_large, /* test_accept */ true); + BOOST_CHECK(result_too_large.m_state.IsInvalid()); + BOOST_CHECK_EQUAL(result_too_large.m_state.GetResult(), PackageValidationResult::PCKG_POLICY); + BOOST_CHECK_EQUAL(result_too_large.m_state.GetRejectReason(), "package-too-large"); + + // A single, giant transaction submitted through ProcessNewPackage fails on single tx policy. + CTransactionRef giant_ptx = create_placeholder_tx(999, 999); + BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_PACKAGE_SIZE * 1000); + auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /* test_accept */ true); + BOOST_CHECK(result_single_large.m_state.IsInvalid()); + BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX); + BOOST_CHECK_EQUAL(result_single_large.m_state.GetRejectReason(), "transaction failed"); + auto it_giant_tx = result_single_large.m_tx_results.find(giant_ptx->GetWitnessHash()); + BOOST_CHECK(it_giant_tx != result_single_large.m_tx_results.end()); + BOOST_CHECK_EQUAL(it_giant_tx->second.m_state.GetRejectReason(), "tx-size"); + + // Check that mempool size hasn't changed. + BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 7bf7f9e0ba..0ac178443a 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -263,7 +263,8 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio int input_height, CKey input_signing_key, CScript output_destination, - CAmount output_amount) + CAmount output_amount, + bool submit) { // Transaction we will submit to the mempool CMutableTransaction mempool_txn; @@ -296,8 +297,8 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio std::map<int, std::string> input_errors; assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors)); - // Add transaction to the mempool - { + // If submit=true, add transaction to the mempool. + if (submit) { LOCK(cs_main); const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false); assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID); diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index b19dd75765..5d12dc2323 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -135,13 +135,15 @@ struct TestChain100Setup : public RegTestingSetup { * @param input_signing_key The key to spend the input_transaction * @param output_destination Where to send the output * @param output_amount How much to send + * @param submit Whether or not to submit to mempool */ CMutableTransaction CreateValidMempoolTransaction(CTransactionRef input_transaction, int input_vout, int input_height, CKey input_signing_key, CScript output_destination, - CAmount output_amount = CAmount(1 * COIN)); + CAmount output_amount = CAmount(1 * COIN), + bool submit = true); ~TestChain100Setup(); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 5957637e81..4413da7ea7 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -515,7 +515,9 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags) LockPoints lp = it->GetLockPoints(); assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp); - if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags) || !CheckSequenceLocks(active_chainstate, *this, tx, flags, &lp, validLP)) { + CCoinsViewMemPool viewMempool(&active_chainstate.CoinsTip(), *this); + if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags) + || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), viewMempool, tx, flags, &lp, validLP)) { // Note if CheckSequenceLocks fails the LockPoints may still be invalid // So it's critical that we remove the tx and not depend on the LockPoints. txToRemove.insert(it); @@ -920,6 +922,13 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { + // Check to see if the inputs are made available by another tx in the package. + // These Coins would not be available in the underlying CoinsView. + if (auto it = m_temp_added.find(outpoint); it != m_temp_added.end()) { + coin = it->second; + return true; + } + // If an entry in the mempool exists, always return that one, as it's guaranteed to never // conflict with the underlying cache, and it cannot have pruned entries (as it contains full) // transactions. First checking the underlying cache risks returning a pruned entry instead. @@ -935,6 +944,13 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); } +void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx) +{ + for (unsigned int n = 0; n < tx->vout.size(); ++n) { + m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false)); + } +} + size_t CTxMemPool::DynamicMemoryUsage() const { LOCK(cs); // Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented. diff --git a/src/txmempool.h b/src/txmempool.h index 594b4981f6..46b89049bb 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -852,7 +852,8 @@ public: * CCoinsView that brings transactions from a mempool into view. * It does not check for spendings by memory pool transactions. * Instead, it provides access to all Coins which are either unspent in the - * base CCoinsView, or are outputs from any mempool transaction! + * base CCoinsView, are outputs from any mempool transaction, or are + * tracked temporarily to allow transaction dependencies in package validation. * This allows transaction replacement to work as expected, as you want to * have all inputs "available" to check signatures, and any cycles in the * dependency graph are checked directly in AcceptToMemoryPool. @@ -862,12 +863,19 @@ public: */ class CCoinsViewMemPool : public CCoinsViewBacked { + /** + * Coins made available by transactions being validated. Tracking these allows for package + * validation, since we can access transaction outputs without submitting them to mempool. + */ + std::unordered_map<COutPoint, Coin, SaltedOutpointHasher> m_temp_added; protected: const CTxMemPool& mempool; public: CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn); bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; + /** Add the coins created by this transaction. */ + void PackageAddTransaction(const CTransactionRef& tx); }; /** diff --git a/src/util/serfloat.cpp b/src/util/serfloat.cpp new file mode 100644 index 0000000000..8edca924cd --- /dev/null +++ b/src/util/serfloat.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <util/serfloat.h> + +#include <cmath> +#include <limits> + +double DecodeDouble(uint64_t v) noexcept { + static constexpr double NANVAL = std::numeric_limits<double>::quiet_NaN(); + static constexpr double INFVAL = std::numeric_limits<double>::infinity(); + double sign = 1.0; + if (v & 0x8000000000000000) { + sign = -1.0; + v ^= 0x8000000000000000; + } + // Zero + if (v == 0) return copysign(0.0, sign); + // Infinity + if (v == 0x7ff0000000000000) return copysign(INFVAL, sign); + // Other numbers + int exp = (v & 0x7FF0000000000000) >> 52; + uint64_t man = v & 0xFFFFFFFFFFFFF; + if (exp == 2047) { + // NaN + return NANVAL; + } else if (exp == 0) { + // Subnormal + return copysign(ldexp((double)man, -1074), sign); + } else { + // Normal + return copysign(ldexp((double)(man + 0x10000000000000), -1075 + exp), sign); + } +} + +uint64_t EncodeDouble(double f) noexcept { + int cls = std::fpclassify(f); + uint64_t sign = 0; + if (copysign(1.0, f) == -1.0) { + f = -f; + sign = 0x8000000000000000; + } + // Zero + if (cls == FP_ZERO) return sign; + // Infinity + if (cls == FP_INFINITE) return sign | 0x7ff0000000000000; + // NaN + if (cls == FP_NAN) return 0x7ff8000000000000; + // Other numbers + int exp; + uint64_t man = std::round(std::frexp(f, &exp) * 9007199254740992.0); + if (exp < -1021) { + // Too small to represent, encode 0 + if (exp < -1084) return sign; + // Subnormal numbers + return sign | (man >> (-1021 - exp)); + } else { + // Too big to represent, encode infinity + if (exp > 1024) return sign | 0x7ff0000000000000; + // Normal numbers + return sign | (((uint64_t)(1022 + exp)) << 52) | (man & 0xFFFFFFFFFFFFF); + } +} diff --git a/src/util/serfloat.h b/src/util/serfloat.h new file mode 100644 index 0000000000..4d912b0176 --- /dev/null +++ b/src/util/serfloat.h @@ -0,0 +1,16 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_SERFLOAT_H +#define BITCOIN_UTIL_SERFLOAT_H + +#include <stdint.h> + +/* Encode a double using the IEEE 754 binary64 format. All NaNs are encoded as x86/ARM's + * positive quiet NaN with payload 0. */ +uint64_t EncodeDouble(double f) noexcept; +/* Reverse operation of DecodeDouble. DecodeDouble(EncodeDouble(f))==f unless isnan(f). */ +double DecodeDouble(uint64_t v) noexcept; + +#endif // BITCOIN_UTIL_SERFLOAT_H diff --git a/src/validation.cpp b/src/validation.cpp index f591e64fd4..86539ab01f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -42,6 +42,7 @@ #include <uint256.h> #include <undo.h> #include <util/check.h> // For NDEBUG compile time check +#include <util/hasher.h> #include <util/moneystr.h> #include <util/rbf.h> #include <util/strencodings.h> @@ -50,6 +51,7 @@ #include <validationinterface.h> #include <warnings.h> +#include <numeric> #include <optional> #include <string> @@ -245,18 +247,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) return true; } -bool CheckSequenceLocks(CChainState& active_chainstate, - const CTxMemPool& pool, +bool CheckSequenceLocks(CBlockIndex* tip, + const CCoinsView& coins_view, const CTransaction& tx, int flags, LockPoints* lp, bool useExistingLockPoints) { - AssertLockHeld(cs_main); - AssertLockHeld(pool.cs); - assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); - - CBlockIndex* tip = active_chainstate.m_chain.Tip(); assert(tip != nullptr); CBlockIndex index; @@ -276,14 +273,12 @@ bool CheckSequenceLocks(CChainState& active_chainstate, lockPair.second = lp->time; } else { - // CoinsTip() contains the UTXO set for active_chainstate.m_chain.Tip() - CCoinsViewMemPool viewMemPool(&active_chainstate.CoinsTip(), pool); std::vector<int> prevheights; prevheights.resize(tx.vin.size()); for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { const CTxIn& txin = tx.vin[txinIndex]; Coin coin; - if (!viewMemPool.GetCoin(txin.prevout, coin)) { + if (!coins_view.GetCoin(txin.prevout, coin)) { return error("%s: Missing input", __func__); } if (coin.nHeight == MEMPOOL_HEIGHT) { @@ -477,11 +472,20 @@ public: */ std::vector<COutPoint>& m_coins_to_uncache; const bool m_test_accept; + /** Disable BIP125 RBFing; disallow all conflicts with mempool transactions. */ + const bool disallow_mempool_conflicts; }; // Single transaction acceptance MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + /** + * Multiple transaction acceptance. Transactions may or may not be interdependent, + * but must not conflict with each other. Parents must come before children if any + * dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned. + */ + PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + private: // All the intermediate state that gets passed between the various levels // of checking a given transaction. @@ -638,7 +642,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) break; } } - if (fReplacementOptOut) { + if (fReplacementOptOut || args.disallow_mempool_conflicts) { return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict"); } @@ -686,10 +690,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // Only accept BIP68 sequence locked transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. - // Must keep pool.cs for this unless we change CheckSequenceLocks to take a - // CoinsViewCache instead of create its own + // Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's + // backend was removed, it no longer pulls coins from the mempool. assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate)); - if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) + if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final"); assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman)); @@ -1045,7 +1049,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef Workspace ws(ptx); - if (!PreChecks(args, ws)) return MempoolAcceptResult(ws.m_state); + if (!PreChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state); // Only compute the precomputed transaction data if we need to verify // scripts (ie, other policy checks pass). We perform the inexpensive @@ -1053,20 +1057,121 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef // checks pass, to mitigate CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata; - if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state); + if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult::Failure(ws.m_state); - if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state); + if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult::Failure(ws.m_state); // Tx was accepted, but not added if (args.m_test_accept) { - return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees); + return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees); } - if (!Finalize(args, ws)) return MempoolAcceptResult(ws.m_state); + if (!Finalize(args, ws)) return MempoolAcceptResult::Failure(ws.m_state); GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence()); - return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees); + return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees); +} + +PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) +{ + AssertLockHeld(cs_main); + + PackageValidationState package_state; + const unsigned int package_count = txns.size(); + + // These context-free package limits can be checked before taking the mempool lock. + if (package_count > MAX_PACKAGE_COUNT) { + package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions"); + return PackageMempoolAcceptResult(package_state, {}); + } + + const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0, + [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); }); + // If the package only contains 1 tx, it's better to report the policy violation on individual tx size. + if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) { + package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large"); + return PackageMempoolAcceptResult(package_state, {}); + } + + // Construct workspaces and check package policies. + std::vector<Workspace> workspaces{}; + workspaces.reserve(package_count); + { + std::unordered_set<uint256, SaltedTxidHasher> later_txids; + std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()), + [](const auto& tx) { return tx->GetHash(); }); + // Require the package to be sorted in order of dependency, i.e. parents appear before children. + // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and + // fail on something less ambiguous (missing-inputs could also be an orphan or trying to + // spend nonexistent coins). + for (const auto& tx : txns) { + for (const auto& input : tx->vin) { + if (later_txids.find(input.prevout.hash) != later_txids.end()) { + // The parent is a subsequent transaction in the package. + package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted"); + return PackageMempoolAcceptResult(package_state, {}); + } + } + later_txids.erase(tx->GetHash()); + workspaces.emplace_back(Workspace(tx)); + } + } + std::map<const uint256, const MempoolAcceptResult> results; + { + // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package. + std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen; + for (const auto& tx : txns) { + for (const auto& input : tx->vin) { + if (inputs_seen.find(input.prevout) != inputs_seen.end()) { + // This input is also present in another tx in the package. + package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package"); + return PackageMempoolAcceptResult(package_state, {}); + } + } + // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could + // catch duplicate inputs within a single tx. This is a more severe, consensus error, + // and we want to report that from CheckTransaction instead. + std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()), + [](const auto& input) { return input.prevout; }); + } + } + + LOCK(m_pool.cs); + + // Do all PreChecks first and fail fast to avoid running expensive script checks when unnecessary. + for (Workspace& ws : workspaces) { + if (!PreChecks(args, ws)) { + package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed"); + // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished. + results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state)); + return PackageMempoolAcceptResult(package_state, std::move(results)); + } + // Make the coins created by this transaction available for subsequent transactions in the + // package to spend. Since we already checked conflicts in the package and RBFs are + // impossible, we don't need to track the coins spent. Note that this logic will need to be + // updated if RBFs in packages are allowed in the future. + assert(args.disallow_mempool_conflicts); + m_viewmempool.PackageAddTransaction(ws.m_ptx); + } + + for (Workspace& ws : workspaces) { + PrecomputedTransactionData txdata; + if (!PolicyScriptChecks(args, ws, txdata)) { + // Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished. + package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed"); + results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state)); + return PackageMempoolAcceptResult(package_state, std::move(results)); + } + if (args.m_test_accept) { + // When test_accept=true, transactions that pass PolicyScriptChecks are valid because there are + // no further mempool checks (passing PolicyScriptChecks implies passing ConsensusScriptChecks). + results.emplace(ws.m_ptx->GetWitnessHash(), + MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees)); + } + } + + return PackageMempoolAcceptResult(package_state, std::move(results)); } } // anon namespace @@ -1079,7 +1184,8 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::vector<COutPoint> coins_to_uncache; - MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept }; + MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, + test_accept, /* disallow_mempool_conflicts */ false }; assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args); @@ -1105,6 +1211,29 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept); } +PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool, + const Package& package, bool test_accept) +{ + AssertLockHeld(cs_main); + assert(test_accept); // Only allow package accept dry-runs (testmempoolaccept RPC). + assert(!package.empty()); + assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;})); + + std::vector<COutPoint> coins_to_uncache; + const CChainParams& chainparams = Params(); + MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache, + test_accept, /* disallow_mempool_conflicts */ true }; + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args); + + // Uncache coins pertaining to transactions that were not submitted to the mempool. + // Ensure the cache is still within its size limits. + for (const COutPoint& hashTx : coins_to_uncache) { + active_chainstate.CoinsTip().Uncache(hashTx); + } + return result; +} + CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock) { LOCK(cs_main); diff --git a/src/validation.h b/src/validation.h index 1b50644185..43e1c5c22e 100644 --- a/src/validation.h +++ b/src/validation.h @@ -18,6 +18,7 @@ #include <fs.h> #include <node/utxo_snapshot.h> #include <policy/feerate.h> +#include <policy/packages.h> #include <protocol.h> // For CMessageHeader::MessageStartChars #include <script/script_error.h> #include <sync.h> @@ -82,8 +83,6 @@ static constexpr bool DEFAULT_COINSTATSINDEX{false}; static const char* const DEFAULT_BLOCKFILTERINDEX = "0"; /** Default for -persistmempool */ static const bool DEFAULT_PERSIST_MEMPOOL = true; -/** Default for using fee filter */ -static const bool DEFAULT_FEEFILTER = true; /** Default for -stopatheight */ static const int DEFAULT_STOPATHEIGHT = 0; /** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of ::ChainActive().Tip() will not be pruned. */ @@ -169,9 +168,7 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh * Validation result for a single transaction mempool acceptance. */ struct MempoolAcceptResult { - /** Used to indicate the results of mempool validation, - * including the possibility of unfinished validation. - */ + /** Used to indicate the results of mempool validation. */ enum class ResultType { VALID, //!> Fully validated, valid. INVALID, //!> Invalid. @@ -184,7 +181,16 @@ struct MempoolAcceptResult { const std::optional<std::list<CTransactionRef>> m_replaced_transactions; /** Raw base fees in satoshis. */ const std::optional<CAmount> m_base_fees; + static MempoolAcceptResult Failure(TxValidationState state) { + return MempoolAcceptResult(state); + } + static MempoolAcceptResult Success(std::list<CTransactionRef>&& replaced_txns, CAmount fees) { + return MempoolAcceptResult(std::move(replaced_txns), fees); + } + +// Private constructors. Use static methods MempoolAcceptResult::Success, etc. to construct. +private: /** Constructor for failure case */ explicit MempoolAcceptResult(TxValidationState state) : m_result_type(ResultType::INVALID), m_state(state) { @@ -198,6 +204,28 @@ struct MempoolAcceptResult { }; /** +* Validation result for package mempool acceptance. +*/ +struct PackageMempoolAcceptResult +{ + const PackageValidationState m_state; + /** + * Map from wtxid to finished MempoolAcceptResults. The client is responsible + * for keeping track of the transaction objects themselves. If a result is not + * present, it means validation was unfinished for that transaction. + */ + std::map<const uint256, const MempoolAcceptResult> m_tx_results; + + explicit PackageMempoolAcceptResult(PackageValidationState state, + std::map<const uint256, const MempoolAcceptResult>&& results) + : m_state{state}, m_tx_results(std::move(results)) {} + + /** Constructor to create a PackageMempoolAcceptResult from a single MempoolAcceptResult */ + explicit PackageMempoolAcceptResult(const uint256& wtxid, const MempoolAcceptResult& result) + : m_tx_results{ {wtxid, result} } {} +}; + +/** * (Try to) add a transaction to the memory pool. * @param[in] bypass_limits When true, don't enforce mempool fee limits. * @param[in] test_accept When true, run validation checks but don't submit to mempool. @@ -205,6 +233,18 @@ struct MempoolAcceptResult { MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx, bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +/** +* Atomically test acceptance of a package. If the package only contains one tx, package rules still apply. +* @param[in] txns Group of transactions which may be independent or contain +* parent-child dependencies. The transactions must not conflict, i.e. +* must not spend the same inputs, even if it would be a valid BIP125 +* replace-by-fee. Parents must appear before children. +* @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction. +* If a transaction fails, validation will exit early and some results may be missing. +*/ +PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool, + const Package& txns, bool test_accept) + EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Apply the effects of this transaction on the UTXO set represented by view */ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); @@ -226,9 +266,13 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** - * Check if transaction will be BIP 68 final in the next block to be created. - * - * Simulates calling SequenceLocks() with data from the tip of the current active chain. + * Check if transaction will be BIP68 final in the next block to be created on top of tip. + * @param[in] tip Chain tip to check tx sequence locks against. For example, + * the tip of the current active chain. + * @param[in] coins_view Any CCoinsView that provides access to the relevant coins + * for checking sequence locks. Any CCoinsView can be passed in; + * it is assumed to be consistent with the tip. + * Simulates calling SequenceLocks() with data from the tip passed in. * Optionally stores in LockPoints the resulting height and time calculated and the hash * of the block needed for calculation or skips the calculation and uses the LockPoints * passed in for evaluation. @@ -236,12 +280,12 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE * * See consensus/consensus.h for flag definitions. */ -bool CheckSequenceLocks(CChainState& active_chainstate, - const CTxMemPool& pool, +bool CheckSequenceLocks(CBlockIndex* tip, + const CCoinsView& coins_view, const CTransaction& tx, int flags, LockPoints* lp = nullptr, - bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs); + bool useExistingLockPoints = false); /** * Closure representing one script verification diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 5a18308a73..6d502e1df1 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -14,7 +14,7 @@ struct { bool operator()(const OutputGroup& a, const OutputGroup& b) const { - return a.effective_value > b.effective_value; + return a.GetSelectionAmount() > b.GetSelectionAmount(); } } descending; @@ -49,37 +49,34 @@ struct { * @param const std::vector<CInputCoin>& utxo_pool The set of UTXOs that we are choosing from. * These UTXOs will be sorted in descending order by effective value and the CInputCoins' * values are their effective values. - * @param const CAmount& target_value This is the value that we want to select. It is the lower + * @param const CAmount& selection_target This is the value that we want to select. It is the lower * bound of the range. * @param const CAmount& cost_of_change This is the cost of creating and spending a change output. - * This plus target_value is the upper bound of the range. + * This plus selection_target is the upper bound of the range. * @param std::set<CInputCoin>& out_set -> This is an output parameter for the set of CInputCoins * that have been selected. * @param CAmount& value_ret -> This is an output parameter for the total value of the CInputCoins * that were selected. - * @param CAmount not_input_fees -> The fees that need to be paid for the outputs and fixed size - * overhead (version, locktime, marker and flag) */ static const size_t TOTAL_TRIES = 100000; -bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees) +bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret) { out_set.clear(); CAmount curr_value = 0; std::vector<bool> curr_selection; // select the utxo at this index curr_selection.reserve(utxo_pool.size()); - CAmount actual_target = not_input_fees + target_value; // Calculate curr_available_value CAmount curr_available_value = 0; for (const OutputGroup& utxo : utxo_pool) { // Assert that this utxo is not negative. It should never be negative, effective value calculation should have removed it - assert(utxo.effective_value > 0); - curr_available_value += utxo.effective_value; + assert(utxo.GetSelectionAmount() > 0); + curr_available_value += utxo.GetSelectionAmount(); } - if (curr_available_value < actual_target) { + if (curr_available_value < selection_target) { return false; } @@ -94,12 +91,12 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v for (size_t i = 0; i < TOTAL_TRIES; ++i) { // Conditions for starting a backtrack bool backtrack = false; - if (curr_value + curr_available_value < actual_target || // Cannot possibly reach target with the amount remaining in the curr_available_value. - curr_value > actual_target + cost_of_change || // Selected value is out of range, go back and try other branch + if (curr_value + curr_available_value < selection_target || // Cannot possibly reach target with the amount remaining in the curr_available_value. + curr_value > selection_target + cost_of_change || // Selected value is out of range, go back and try other branch (curr_waste > best_waste && (utxo_pool.at(0).fee - utxo_pool.at(0).long_term_fee) > 0)) { // Don't select things which we know will be more wasteful if the waste is increasing backtrack = true; - } else if (curr_value >= actual_target) { // Selected value is within range - curr_waste += (curr_value - actual_target); // This is the excess value which is added to the waste for the below comparison + } else if (curr_value >= selection_target) { // Selected value is within range + curr_waste += (curr_value - selection_target); // This is the excess value which is added to the waste for the below comparison // Adding another UTXO after this check could bring the waste down if the long term fee is higher than the current fee. // However we are not going to explore that because this optimization for the waste is only done when we have hit our target // value. Adding any more UTXOs will be just burning the UTXO; it will go entirely to fees. Thus we aren't going to @@ -112,7 +109,7 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v break; } } - curr_waste -= (curr_value - actual_target); // Remove the excess value as we will be selecting different coins now + curr_waste -= (curr_value - selection_target); // Remove the excess value as we will be selecting different coins now backtrack = true; } @@ -121,7 +118,7 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v // Walk backwards to find the last included UTXO that still needs to have its omission branch traversed. while (!curr_selection.empty() && !curr_selection.back()) { curr_selection.pop_back(); - curr_available_value += utxo_pool.at(curr_selection.size()).effective_value; + curr_available_value += utxo_pool.at(curr_selection.size()).GetSelectionAmount(); } if (curr_selection.empty()) { // We have walked back to the first utxo and no branch is untraversed. All solutions searched @@ -131,24 +128,24 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v // Output was included on previous iterations, try excluding now. curr_selection.back() = false; OutputGroup& utxo = utxo_pool.at(curr_selection.size() - 1); - curr_value -= utxo.effective_value; + curr_value -= utxo.GetSelectionAmount(); curr_waste -= utxo.fee - utxo.long_term_fee; } else { // Moving forwards, continuing down this branch OutputGroup& utxo = utxo_pool.at(curr_selection.size()); // Remove this utxo from the curr_available_value utxo amount - curr_available_value -= utxo.effective_value; + curr_available_value -= utxo.GetSelectionAmount(); // Avoid searching a branch if the previous UTXO has the same value and same waste and was excluded. Since the ratio of fee to // long term fee is the same, we only need to check if one of those values match in order to know that the waste is the same. if (!curr_selection.empty() && !curr_selection.back() && - utxo.effective_value == utxo_pool.at(curr_selection.size() - 1).effective_value && + utxo.GetSelectionAmount() == utxo_pool.at(curr_selection.size() - 1).GetSelectionAmount() && utxo.fee == utxo_pool.at(curr_selection.size() - 1).fee) { curr_selection.push_back(false); } else { // Inclusion branch first (Largest First Exploration) curr_selection.push_back(true); - curr_value += utxo.effective_value; + curr_value += utxo.GetSelectionAmount(); curr_waste += utxo.fee - utxo.long_term_fee; } } @@ -230,14 +227,14 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group Shuffle(groups.begin(), groups.end(), FastRandomContext()); for (const OutputGroup& group : groups) { - if (group.m_value == nTargetValue) { + if (group.GetSelectionAmount() == nTargetValue) { util::insert(setCoinsRet, group.m_outputs); nValueRet += group.m_value; return true; - } else if (group.m_value < nTargetValue + MIN_CHANGE) { + } else if (group.GetSelectionAmount() < nTargetValue + MIN_CHANGE) { applicable_groups.push_back(group); - nTotalLower += group.m_value; - } else if (!lowest_larger || group.m_value < lowest_larger->m_value) { + nTotalLower += group.GetSelectionAmount(); + } else if (!lowest_larger || group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) { lowest_larger = group; } } @@ -270,7 +267,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, // or the next bigger coin is closer), return the bigger coin if (lowest_larger && - ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->m_value <= nBest)) { + ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->GetSelectionAmount() <= nBest)) { util::insert(setCoinsRet, lowest_larger->m_outputs); nValueRet += lowest_larger->m_value; } else { @@ -339,3 +336,8 @@ bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_f && m_ancestors <= eligibility_filter.max_ancestors && m_descendants <= eligibility_filter.max_descendants; } + +CAmount OutputGroup::GetSelectionAmount() const +{ + return m_subtract_fee_outputs ? m_value : effective_value; +} diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index 5645e6db46..7a3fb82139 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -57,6 +57,47 @@ public: } }; +/** Parameters for one iteration of Coin Selection. */ +struct CoinSelectionParams +{ + /** Size of a change output in bytes, determined by the output type. */ + size_t change_output_size = 0; + /** Size of the input to spend a change output in virtual bytes. */ + size_t change_spend_size = 0; + /** Cost of creating the change output. */ + CAmount m_change_fee{0}; + /** Cost of creating the change output + cost of spending the change output in the future. */ + CAmount m_cost_of_change{0}; + /** The targeted feerate of the transaction being built. */ + CFeeRate m_effective_feerate; + /** The feerate estimate used to estimate an upper bound on what should be sufficient to spend + * the change output sometime in the future. */ + CFeeRate m_long_term_feerate; + /** If the cost to spend a change output at the discard feerate exceeds its value, drop it to fees. */ + CFeeRate m_discard_feerate; + /** Size of the transaction before coin selection, consisting of the header and recipient + * output(s), excluding the inputs and change output(s). */ + size_t tx_noinputs_size = 0; + /** Indicate that we are subtracting the fee from outputs */ + bool m_subtract_fee_outputs = false; + /** When true, always spend all (up to OUTPUT_GROUP_MAX_ENTRIES) or none of the outputs + * associated with the same address. This helps reduce privacy leaks resulting from address + * reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */ + bool m_avoid_partial_spends = false; + + CoinSelectionParams(size_t change_output_size, size_t change_spend_size, CFeeRate effective_feerate, + CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size, bool avoid_partial) : + change_output_size(change_output_size), + change_spend_size(change_spend_size), + m_effective_feerate(effective_feerate), + m_long_term_feerate(long_term_feerate), + m_discard_feerate(discard_feerate), + tx_noinputs_size(tx_noinputs_size), + m_avoid_partial_spends(avoid_partial) + {} + CoinSelectionParams() {} +}; + /** Parameters for filtering which OutputGroups we may use in coin selection. * We start by being very selective and requiring multiple confirmations and * then get more permissive if we cannot fund the transaction. */ @@ -109,18 +150,23 @@ struct OutputGroup * a lower feerate). Calculated using long term fee estimate. This is used to decide whether * it could be economical to create a change output. */ CFeeRate m_long_term_feerate{0}; + /** Indicate that we are subtracting the fee from outputs. + * When true, the value that is used for coin selection is the UTXO's real value rather than effective value */ + bool m_subtract_fee_outputs{false}; OutputGroup() {} - OutputGroup(const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate) : - m_effective_feerate(effective_feerate), - m_long_term_feerate(long_term_feerate) + OutputGroup(const CoinSelectionParams& params) : + m_effective_feerate(params.m_effective_feerate), + m_long_term_feerate(params.m_long_term_feerate), + m_subtract_fee_outputs(params.m_subtract_fee_outputs) {} void Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only); bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const; + CAmount GetSelectionAmount() const; }; -bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret, CAmount not_input_fees); +bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret); // Original coin selection algorithm as a fallback bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 08adf09df4..30fef50c3b 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -190,7 +190,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo if (coin_control.m_feerate) { // The user provided a feeRate argument. // We calculate this here to avoid compiler warning on the cs_wallet lock - const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet).first; + const int64_t maxTxSize{CalculateMaximumSignedTxSize(*wtx.tx, &wallet).vsize}; Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, errors); if (res != Result::OK) { return res; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 632aae87c9..eb0d6316c0 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -43,7 +43,7 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit(); void WalletInit::AddWalletOptions(ArgsManager& argsman) const { argsman.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); - argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u (always enabled for wallets with \"avoid_reuse\" enabled))", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + argsman.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting many (possibly all) or none, instead of selecting on a per-output basis. Privacy is improved as addresses are mostly swept with fewer transactions and outputs are aggregated in clean change addresses. It may result in higher fees due to less optimal coin selection caused by this added limitation and possibly a larger-than-necessary number of inputs being used. Always enabled for wallets with \"avoid_reuse\" enabled, otherwise default: %u.", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kvB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index e0df96666f..dbf9fd46b6 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -105,7 +105,7 @@ bool LoadWallets(interfaces::Chain& chain) if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) { continue; } - chain.initMessage(_("Loading wallet...").translated); + chain.initMessage(_("Loading wallet…").translated); std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(&chain, name, std::move(database), options.create_flags, error, warnings) : nullptr; if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n"))); if (!pwallet) { diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp new file mode 100644 index 0000000000..de81dbf324 --- /dev/null +++ b/src/wallet/receive.cpp @@ -0,0 +1,471 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/consensus.h> +#include <wallet/receive.h> +#include <wallet/transaction.h> +#include <wallet/wallet.h> + +isminetype CWallet::IsMine(const CTxIn &txin) const +{ + AssertLockHeld(cs_wallet); + std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.tx->vout.size()) + return IsMine(prev.tx->vout[txin.prevout.n]); + } + return ISMINE_NO; +} + +bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const +{ + LOCK(cs_wallet); + + for (const CTxIn& txin : tx.vin) + { + auto mi = mapWallet.find(txin.prevout.hash); + if (mi == mapWallet.end()) + return false; // any unknown inputs can't be from us + + const CWalletTx& prev = (*mi).second; + + if (txin.prevout.n >= prev.tx->vout.size()) + return false; // invalid input! + + if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) + return false; + } + return true; +} + +CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const +{ + if (!MoneyRange(txout.nValue)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + LOCK(cs_wallet); + return ((IsMine(txout) & filter) ? txout.nValue : 0); +} + +CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const +{ + CAmount nCredit = 0; + for (const CTxOut& txout : tx.vout) + { + nCredit += GetCredit(txout, filter); + if (!MoneyRange(nCredit)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nCredit; +} + +bool CWallet::IsChange(const CScript& script) const +{ + // TODO: fix handling of 'change' outputs. The assumption is that any + // payment to a script that is ours, but is not in the address book + // is change. That assumption is likely to break when we implement multisignature + // wallets that return change back into a multi-signature-protected address; + // a better way of identifying which outputs are 'the send' and which are + // 'the change' will need to be implemented (maybe extend CWalletTx to remember + // which output, if any, was change). + AssertLockHeld(cs_wallet); + if (IsMine(script)) + { + CTxDestination address; + if (!ExtractDestination(script, address)) + return true; + if (!FindAddressBookEntry(address)) { + return true; + } + } + return false; +} + +bool CWallet::IsChange(const CTxOut& txout) const +{ + return IsChange(txout.scriptPubKey); +} + +CAmount CWallet::GetChange(const CTxOut& txout) const +{ + AssertLockHeld(cs_wallet); + if (!MoneyRange(txout.nValue)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + return (IsChange(txout) ? txout.nValue : 0); +} + +CAmount CWallet::GetChange(const CTransaction& tx) const +{ + LOCK(cs_wallet); + CAmount nChange = 0; + for (const CTxOut& txout : tx.vout) + { + nChange += GetChange(txout); + if (!MoneyRange(nChange)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nChange; +} + +CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const +{ + auto& amount = m_amounts[type]; + if (recalculate || !amount.m_cached[filter]) { + amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter)); + m_is_cache_empty = false; + } + return amount.m_value[filter]; +} + +CAmount CWalletTx::GetCredit(const isminefilter& filter) const +{ + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsImmatureCoinBase()) + return 0; + + CAmount credit = 0; + if (filter & ISMINE_SPENDABLE) { + // GetBalance can assume transactions in mapWallet won't change + credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE); + } + if (filter & ISMINE_WATCH_ONLY) { + credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY); + } + return credit; +} + +CAmount CWalletTx::GetDebit(const isminefilter& filter) const +{ + if (tx->vin.empty()) + return 0; + + CAmount debit = 0; + if (filter & ISMINE_SPENDABLE) { + debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE); + } + if (filter & ISMINE_WATCH_ONLY) { + debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY); + } + return debit; +} + +CAmount CWalletTx::GetChange() const +{ + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*tx); + fChangeCached = true; + return nChangeCached; +} + +CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const +{ + if (IsImmatureCoinBase() && IsInMainChain()) { + return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache); + } + + return 0; +} + +CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const +{ + if (IsImmatureCoinBase() && IsInMainChain()) { + return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache); + } + + return 0; +} + +CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const +{ + if (pwallet == nullptr) + return 0; + + // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future). + bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsImmatureCoinBase()) + return 0; + + if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) { + return m_amounts[AVAILABLE_CREDIT].m_value[filter]; + } + + bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < tx->vout.size(); i++) + { + if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) { + const CTxOut &txout = tx->vout[i]; + nCredit += pwallet->GetCredit(txout, filter); + if (!MoneyRange(nCredit)) + throw std::runtime_error(std::string(__func__) + " : value out of range"); + } + } + + if (allow_cache) { + m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit); + m_is_cache_empty = false; + } + + return nCredit; +} + +void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, + std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const +{ + nFee = 0; + listReceived.clear(); + listSent.clear(); + + // Compute fee: + CAmount nDebit = GetDebit(filter); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + CAmount nValueOut = tx->GetValueOut(); + nFee = nDebit - nValueOut; + } + + LOCK(pwallet->cs_wallet); + // Sent/received. + for (unsigned int i = 0; i < tx->vout.size(); ++i) + { + const CTxOut& txout = tx->vout[i]; + isminetype fIsMine = pwallet->IsMine(txout); + // Only need to handle txouts if AT LEAST one of these is true: + // 1) they debit from us (sent) + // 2) the output is to us (received) + if (nDebit > 0) + { + // Don't report 'change' txouts + if (pwallet->IsChange(txout)) + continue; + } + else if (!(fIsMine & filter)) + continue; + + // In either case, we need to get the destination address + CTxDestination address; + + if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable()) + { + pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString()); + address = CNoDestination(); + } + + COutputEntry output = {address, txout.nValue, (int)i}; + + // If we are debited by the transaction, add the output as a "sent" entry + if (nDebit > 0) + listSent.push_back(output); + + // If we are receiving the output, add it as a "received" entry + if (fIsMine & filter) + listReceived.push_back(output); + } + +} + +bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const +{ + AssertLockHeld(cs_wallet); + // Quick answer in most cases + if (!chain().checkFinalTx(*wtx.tx)) return false; + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth >= 1) return true; + if (nDepth < 0) return false; + // using wtx's cached debit + if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false; + + // Don't trust unconfirmed transactions from us unless they are in the mempool. + if (!wtx.InMempool()) return false; + + // Trusted if all inputs are from us and are in the mempool: + for (const CTxIn& txin : wtx.tx->vin) + { + // Transactions not sent by us: not trusted + const CWalletTx* parent = GetWalletTx(txin.prevout.hash); + if (parent == nullptr) return false; + const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; + // Check that this specific input being spent is trusted + if (IsMine(parentOut) != ISMINE_SPENDABLE) return false; + // If we've already trusted this parent, continue + if (trusted_parents.count(parent->GetHash())) continue; + // Recurse to check that the parent is also trusted + if (!IsTrusted(*parent, trusted_parents)) return false; + trusted_parents.insert(parent->GetHash()); + } + return true; +} + +bool CWalletTx::IsTrusted() const +{ + std::set<uint256> trusted_parents; + LOCK(pwallet->cs_wallet); + return pwallet->IsTrusted(*this, trusted_parents); +} + +CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const +{ + Balance ret; + isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED; + { + LOCK(cs_wallet); + std::set<uint256> trusted_parents; + for (const auto& entry : mapWallet) + { + const CWalletTx& wtx = entry.second; + const bool is_trusted{IsTrusted(wtx, trusted_parents)}; + const int tx_depth{wtx.GetDepthInMainChain()}; + const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)}; + const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)}; + if (is_trusted && tx_depth >= min_depth) { + ret.m_mine_trusted += tx_credit_mine; + ret.m_watchonly_trusted += tx_credit_watchonly; + } + if (!is_trusted && tx_depth == 0 && wtx.InMempool()) { + ret.m_mine_untrusted_pending += tx_credit_mine; + ret.m_watchonly_untrusted_pending += tx_credit_watchonly; + } + ret.m_mine_immature += wtx.GetImmatureCredit(); + ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(); + } + } + return ret; +} + +std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const +{ + std::map<CTxDestination, CAmount> balances; + + { + LOCK(cs_wallet); + std::set<uint256> trusted_parents; + for (const auto& walletEntry : mapWallet) + { + const CWalletTx& wtx = walletEntry.second; + + if (!IsTrusted(wtx, trusted_parents)) + continue; + + if (wtx.IsImmatureCoinBase()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1)) + continue; + + for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) + { + CTxDestination addr; + if (!IsMine(wtx.tx->vout[i])) + continue; + if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr)) + continue; + + CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue; + balances[addr] += n; + } + } + } + + return balances; +} + +std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const +{ + AssertLockHeld(cs_wallet); + std::set< std::set<CTxDestination> > groupings; + std::set<CTxDestination> grouping; + + for (const auto& walletEntry : mapWallet) + { + const CWalletTx& wtx = walletEntry.second; + + if (wtx.tx->vin.size() > 0) + { + bool any_mine = false; + // group all input addresses with each other + for (const CTxIn& txin : wtx.tx->vin) + { + CTxDestination address; + if(!IsMine(txin)) /* If this input isn't mine, ignore it */ + continue; + if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address)) + continue; + grouping.insert(address); + any_mine = true; + } + + // group change with input addresses + if (any_mine) + { + for (const CTxOut& txout : wtx.tx->vout) + if (IsChange(txout)) + { + CTxDestination txoutAddr; + if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) + continue; + grouping.insert(txoutAddr); + } + } + if (grouping.size() > 0) + { + groupings.insert(grouping); + grouping.clear(); + } + } + + // group lone addrs by themselves + for (const auto& txout : wtx.tx->vout) + if (IsMine(txout)) + { + CTxDestination address; + if(!ExtractDestination(txout.scriptPubKey, address)) + continue; + grouping.insert(address); + groupings.insert(grouping); + grouping.clear(); + } + } + + std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses + std::map< CTxDestination, std::set<CTxDestination>* > setmap; // map addresses to the unique group containing it + for (std::set<CTxDestination> _grouping : groupings) + { + // make a set of all the groups hit by this new group + std::set< std::set<CTxDestination>* > hits; + std::map< CTxDestination, std::set<CTxDestination>* >::iterator it; + for (const CTxDestination& address : _grouping) + if ((it = setmap.find(address)) != setmap.end()) + hits.insert((*it).second); + + // merge all hit groups into a new single group and delete old groups + std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping); + for (std::set<CTxDestination>* hit : hits) + { + merged->insert(hit->begin(), hit->end()); + uniqueGroupings.erase(hit); + delete hit; + } + uniqueGroupings.insert(merged); + + // update setmap + for (const CTxDestination& element : *merged) + setmap[element] = merged; + } + + std::set< std::set<CTxDestination> > ret; + for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings) + { + ret.insert(*uniqueGrouping); + delete uniqueGrouping; + } + + return ret; +} diff --git a/src/wallet/receive.h b/src/wallet/receive.h new file mode 100644 index 0000000000..8eead32413 --- /dev/null +++ b/src/wallet/receive.h @@ -0,0 +1,20 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_RECEIVE_H +#define BITCOIN_WALLET_RECEIVE_H + +#include <amount.h> +#include <wallet/ismine.h> +#include <wallet/transaction.h> +#include <wallet/wallet.h> + +struct COutputEntry +{ + CTxDestination destination; + CAmount amount; + int vout; +}; + +#endif // BITCOIN_WALLET_RECEIVE_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4e3c8ce49d..f270e1ad05 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -447,7 +447,7 @@ static RPCHelpMan sendtoaddress() {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n" " \"" + FeeModes("\"\n\"") + "\""}, {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n" - "dirty if they have previously been used in a transaction."}, + "dirty if they have previously been used in a transaction. If true, this also activates avoidpartialspends, grouping outputs by their addresses."}, {"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."}, {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra information about the transaction."}, }, @@ -3430,12 +3430,10 @@ static RPCHelpMan bumpfee_helper(std::string method_name) "options"}, }, RPCResult{ - RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>( - { - {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction."}, - }, - want_psbt ? std::vector<RPCResult>{} : std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."}} - ), + RPCResult::Type::OBJ, "", "", Cat( + want_psbt ? + std::vector<RPCResult>{{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction."}} : + std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction."}}, { {RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."}, {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."}, @@ -3446,7 +3444,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name) }) }, RPCExamples{ - "\nBump the fee, get the new transaction\'s" + std::string(want_psbt ? "psbt" : "txid") + "\n" + + "\nBump the fee, get the new transaction\'s " + std::string(want_psbt ? "psbt" : "txid") + "\n" + HelpExampleCli(method_name, "<txid>") }, [want_psbt](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue @@ -3528,8 +3526,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name) UniValue result(UniValue::VOBJ); - // If wallet private keys are enabled, return the new transaction id, - // otherwise return the base64-encoded unsigned PSBT of the new transaction. + // For bumpfee, return the new transaction id. + // For psbtbumpfee, return the base64-encoded unsigned PSBT of the new transaction. if (!want_psbt) { if (!feebumper::SignTransaction(*pwallet, mtx)) { throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp new file mode 100644 index 0000000000..97fc7acca5 --- /dev/null +++ b/src/wallet/spend.cpp @@ -0,0 +1,978 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <interfaces/chain.h> +#include <policy/policy.h> +#include <util/check.h> +#include <util/fees.h> +#include <util/moneystr.h> +#include <util/rbf.h> +#include <util/translation.h> +#include <wallet/coincontrol.h> +#include <wallet/fees.h> +#include <wallet/receive.h> +#include <wallet/spend.h> +#include <wallet/transaction.h> +#include <wallet/wallet.h> + +using interfaces::FoundBlock; + +static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100}; + +std::string COutput::ToString() const +{ + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); +} + +int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig) +{ + CMutableTransaction txn; + txn.vin.push_back(CTxIn(COutPoint())); + if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) { + return -1; + } + return GetVirtualTransactionInputSize(txn.vin[0]); +} + +// txouts needs to be in the order of tx.vin +TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig) +{ + CMutableTransaction txNew(tx); + if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) { + return TxSize{-1, -1}; + } + CTransaction ctx(txNew); + int64_t vsize = GetVirtualTransactionSize(ctx); + int64_t weight = GetTransactionWeight(ctx); + return TxSize{vsize, weight}; +} + +TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig) +{ + std::vector<CTxOut> txouts; + for (const CTxIn& input : tx.vin) { + const auto mi = wallet->mapWallet.find(input.prevout.hash); + // Can not estimate size without knowing the input details + if (mi == wallet->mapWallet.end()) { + return TxSize{-1, -1}; + } + assert(input.prevout.n < mi->second.tx->vout.size()); + txouts.emplace_back(mi->second.tx->vout[input.prevout.n]); + } + return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig); +} + +void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const +{ + AssertLockHeld(cs_wallet); + + vCoins.clear(); + CAmount nTotal = 0; + // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where + // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses + bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse); + const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH}; + const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH}; + const bool only_safe = {coinControl ? !coinControl->m_include_unsafe_inputs : true}; + + std::set<uint256> trusted_parents; + for (const auto& entry : mapWallet) + { + const uint256& wtxid = entry.first; + const CWalletTx& wtx = entry.second; + + if (!chain().checkFinalTx(*wtx.tx)) { + continue; + } + + if (wtx.IsImmatureCoinBase()) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !wtx.InMempool()) + continue; + + bool safeTx = IsTrusted(wtx, trusted_parents); + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) { + safeTx = false; + } + + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) { + safeTx = false; + } + + if (only_safe && !safeTx) { + continue; + } + + if (nDepth < min_depth || nDepth > max_depth) { + continue; + } + + for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { + // Only consider selected coins if add_inputs is false + if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) { + continue; + } + + if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount) + continue; + + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) + continue; + + if (IsLockedCoin(entry.first, i)) + continue; + + if (IsSpent(wtxid, i)) + continue; + + isminetype mine = IsMine(wtx.tx->vout[i]); + + if (mine == ISMINE_NO) { + continue; + } + + if (!allow_used_addresses && IsSpentKey(wtxid, i)) { + continue; + } + + std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey); + + bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false; + bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); + + vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly))); + + // Checks the sum amount of all UTXO's. + if (nMinimumSumAmount != MAX_MONEY) { + nTotal += wtx.tx->vout[i].nValue; + + if (nTotal >= nMinimumSumAmount) { + return; + } + } + + // Checks the maximum number of UTXO's. + if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { + return; + } + } + } +} + +CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const +{ + LOCK(cs_wallet); + + CAmount balance = 0; + std::vector<COutput> vCoins; + AvailableCoins(vCoins, coinControl); + for (const COutput& out : vCoins) { + if (out.fSpendable) { + balance += out.tx->tx->vout[out.i].nValue; + } + } + return balance; +} + +const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const +{ + AssertLockHeld(cs_wallet); + const CTransaction* ptx = &tx; + int n = output; + while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) { + const COutPoint& prevout = ptx->vin[0].prevout; + auto it = mapWallet.find(prevout.hash); + if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n || + !IsMine(it->second.tx->vout[prevout.n])) { + break; + } + ptx = it->second.tx.get(); + n = prevout.n; + } + return ptx->vout[n]; +} + +std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const +{ + AssertLockHeld(cs_wallet); + + std::map<CTxDestination, std::vector<COutput>> result; + std::vector<COutput> availableCoins; + + AvailableCoins(availableCoins); + + for (const COutput& coin : availableCoins) { + CTxDestination address; + if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) && + ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) { + result[address].emplace_back(std::move(coin)); + } + } + + std::vector<COutPoint> lockedCoins; + ListLockedCoins(lockedCoins); + // Include watch-only for LegacyScriptPubKeyMan wallets without private keys + const bool include_watch_only = GetLegacyScriptPubKeyMan() && IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); + const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; + for (const COutPoint& output : lockedCoins) { + auto it = mapWallet.find(output.hash); + if (it != mapWallet.end()) { + int depth = it->second.GetDepthInMainChain(); + if (depth >= 0 && output.n < it->second.tx->vout.size() && + IsMine(it->second.tx->vout[output.n]) == is_mine_filter + ) { + CTxDestination address; + if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) { + result[address].emplace_back( + &it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */); + } + } + } + } + + return result; +} + +std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const +{ + std::vector<OutputGroup> groups_out; + + if (!coin_sel_params.m_avoid_partial_spends) { + // Allowing partial spends means no grouping. Each COutput gets its own OutputGroup. + for (const COutput& output : outputs) { + // Skip outputs we cannot spend + if (!output.fSpendable) continue; + + size_t ancestors, descendants; + chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); + CInputCoin input_coin = output.GetInputCoin(); + + // Make an OutputGroup containing just this output + OutputGroup group{coin_sel_params}; + group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); + + // Check the OutputGroup's eligibility. Only add the eligible ones. + if (positive_only && group.GetSelectionAmount() <= 0) continue; + if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); + } + return groups_out; + } + + // We want to combine COutputs that have the same scriptPubKey into single OutputGroups + // except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup. + // To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups. + // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added + // to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has + // OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector. + std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map; + for (const auto& output : outputs) { + // Skip outputs we cannot spend + if (!output.fSpendable) continue; + + size_t ancestors, descendants; + chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); + CInputCoin input_coin = output.GetInputCoin(); + CScript spk = input_coin.txout.scriptPubKey; + + std::vector<OutputGroup>& groups = spk_to_groups_map[spk]; + + if (groups.size() == 0) { + // No OutputGroups for this scriptPubKey yet, add one + groups.emplace_back(coin_sel_params); + } + + // Get the last OutputGroup in the vector so that we can add the CInputCoin to it + // A pointer is used here so that group can be reassigned later if it is full. + OutputGroup* group = &groups.back(); + + // Check if this OutputGroup is full. We limit to OUTPUT_GROUP_MAX_ENTRIES when using -avoidpartialspends + // to avoid surprising users with very high fees. + if (group->m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) { + // The last output group is full, add a new group to the vector and use that group for the insertion + groups.emplace_back(coin_sel_params); + group = &groups.back(); + } + + // Add the input_coin to group + group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); + } + + // Now we go through the entire map and pull out the OutputGroups + for (const auto& spk_and_groups_pair: spk_to_groups_map) { + const std::vector<OutputGroup>& groups_per_spk= spk_and_groups_pair.second; + + // Go through the vector backwards. This allows for the first item we deal with being the partial group. + for (auto group_it = groups_per_spk.rbegin(); group_it != groups_per_spk.rend(); group_it++) { + const OutputGroup& group = *group_it; + + // Don't include partial groups if there are full groups too and we don't want partial groups + if (group_it == groups_per_spk.rbegin() && groups_per_spk.size() > 1 && !filter.m_include_partial_groups) { + continue; + } + + // Check the OutputGroup's eligibility. Only add the eligible ones. + if (positive_only && group.GetSelectionAmount() <= 0) continue; + if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); + } + } + + return groups_out; +} + +bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins, + std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output. + std::vector<OutputGroup> positive_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, true /* positive_only */); + if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, setCoinsRet, nValueRet)) { + return true; + } + // The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here. + std::vector<OutputGroup> all_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, false /* positive_only */); + // While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output. + // So we need to include that for KnapsackSolver as well, as we are expecting to create a change output. + return KnapsackSolver(nTargetValue + coin_selection_params.m_change_fee, all_groups, setCoinsRet, nValueRet); +} + +bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const +{ + std::vector<COutput> vCoins(vAvailableCoins); + CAmount value_to_select = nTargetValue; + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs) + { + for (const COutput& out : vCoins) + { + if (!out.fSpendable) + continue; + nValueRet += out.tx->tx->vout[out.i].nValue; + setCoinsRet.insert(out.GetInputCoin()); + } + return (nValueRet >= nTargetValue); + } + + // calculate value from preset inputs and store them + std::set<CInputCoin> setPresetCoins; + CAmount nValueFromPresetInputs = 0; + + std::vector<COutPoint> vPresetInputs; + coin_control.ListSelected(vPresetInputs); + for (const COutPoint& outpoint : vPresetInputs) + { + std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); + if (it != mapWallet.end()) + { + const CWalletTx& wtx = it->second; + // Clearly invalid input, fail + if (wtx.tx->vout.size() <= outpoint.n) { + return false; + } + // Just to calculate the marginal byte size + CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false)); + nValueFromPresetInputs += coin.txout.nValue; + if (coin.m_input_bytes <= 0) { + return false; // Not solvable, can't estimate size for fee + } + coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes); + if (coin_selection_params.m_subtract_fee_outputs) { + value_to_select -= coin.txout.nValue; + } else { + value_to_select -= coin.effective_value; + } + setPresetCoins.insert(coin); + } else { + return false; // TODO: Allow non-wallet inputs + } + } + + // remove preset inputs from vCoins so that Coin Selection doesn't pick them. + for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();) + { + if (setPresetCoins.count(it->GetInputCoin())) + it = vCoins.erase(it); + else + ++it; + } + + unsigned int limit_ancestor_count = 0; + unsigned int limit_descendant_count = 0; + chain().getPackageLimits(limit_ancestor_count, limit_descendant_count); + const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count); + const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count); + const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + + // form groups from remaining coins; note that preset coins will not + // automatically have their associated (same address) coins included + if (coin_control.m_avoid_partial_spends && vCoins.size() > OUTPUT_GROUP_MAX_ENTRIES) { + // Cases where we have 101+ outputs all pointing to the same destination may result in + // privacy leaks as they will potentially be deterministically sorted. We solve that by + // explicitly shuffling the outputs before processing + Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext()); + } + + // Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the + // transaction at a target feerate. If an attempt fails, more attempts may be made using a more + // permissive CoinEligibilityFilter. + const bool res = [&] { + // Pre-selected inputs already cover the target amount. + if (value_to_select <= 0) return true; + + // If possible, fund the transaction with confirmed UTXOs only. Prefer at least six + // confirmations on outputs received from other wallets and only spend confirmed change. + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; + + // Fall back to using zero confirmation change (but with as few ancestors in the mempool as + // possible) if we cannot fund the transaction otherwise. + if (m_spend_zero_conf_change) { + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), + vCoins, setCoinsRet, nValueRet, coin_selection_params)) { + return true; + } + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), + vCoins, setCoinsRet, nValueRet, coin_selection_params)) { + return true; + } + // If partial groups are allowed, relax the requirement of spending OutputGroups (groups + // of UTXOs sent to the same address, which are obviously controlled by a single wallet) + // in their entirety. + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), + vCoins, setCoinsRet, nValueRet, coin_selection_params)) { + return true; + } + // Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs + // received from other wallets. + if (coin_control.m_include_unsafe_inputs + && SelectCoinsMinConf(value_to_select, + CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), + vCoins, setCoinsRet, nValueRet, coin_selection_params)) { + return true; + } + // Try with unlimited ancestors/descendants. The transaction will still need to meet + // mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but + // OutputGroups use heuristics that may overestimate ancestor/descendant counts. + if (!fRejectLongChains && SelectCoinsMinConf(value_to_select, + CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */), + vCoins, setCoinsRet, nValueRet, coin_selection_params)) { + return true; + } + } + // Coin Selection failed. + return false; + }(); + + // SelectCoinsMinConf clears setCoinsRet, so add the preset inputs from coin_control to the coinset + util::insert(setCoinsRet, setPresetCoins); + + // add preset inputs to the total value selected + nValueRet += nValueFromPresetInputs; + + return res; +} + +static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash) +{ + if (chain.isInitialBlockDownload()) { + return false; + } + constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds + int64_t block_time; + CHECK_NONFATAL(chain.findBlock(block_hash, FoundBlock().time(block_time))); + if (block_time < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) { + return false; + } + return true; +} + +/** + * Return a height-based locktime for new transactions (uses the height of the + * current chain tip unless we are not synced with the current chain + */ +static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height) +{ + uint32_t locktime; + // Discourage fee sniping. + // + // For a large miner the value of the transactions in the best block and + // the mempool can exceed the cost of deliberately attempting to mine two + // blocks to orphan the current best block. By setting nLockTime such that + // only the next block can include the transaction, we discourage this + // practice as the height restricted and limited blocksize gives miners + // considering fee sniping fewer options for pulling off this attack. + // + // A simple way to think about this is from the wallet's point of view we + // always want the blockchain to move forward. By setting nLockTime this + // way we're basically making the statement that we only want this + // transaction to appear in the next block; we don't want to potentially + // encourage reorgs by allowing transactions to appear at lower heights + // than the next block in forks of the best chain. + // + // Of course, the subsidy is high enough, and transaction volume low + // enough, that fee sniping isn't a problem yet, but by implementing a fix + // now we ensure code won't be written that makes assumptions about + // nLockTime that preclude a fix later. + if (IsCurrentForAntiFeeSniping(chain, block_hash)) { + locktime = block_height; + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + locktime = std::max(0, (int)locktime - GetRandInt(100)); + } else { + // If our chain is lagging behind, we can't discourage fee sniping nor help + // the privacy of high-latency transactions. To avoid leaking a potentially + // unique "nLockTime fingerprint", set nLockTime to a constant. + locktime = 0; + } + assert(locktime < LOCKTIME_THRESHOLD); + return locktime; +} + +bool CWallet::CreateTransactionInternal( + const std::vector<CRecipient>& vecSend, + CTransactionRef& tx, + CAmount& nFeeRet, + int& nChangePosInOut, + bilingual_str& error, + const CCoinControl& coin_control, + FeeCalculation& fee_calc_out, + bool sign) +{ + CAmount nValue = 0; + const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); + ReserveDestination reservedest(this, change_type); + unsigned int nSubtractFeeFromAmount = 0; + for (const auto& recipient : vecSend) + { + if (nValue < 0 || recipient.nAmount < 0) + { + error = _("Transaction amounts must not be negative"); + return false; + } + nValue += recipient.nAmount; + + if (recipient.fSubtractFeeFromAmount) + nSubtractFeeFromAmount++; + } + if (vecSend.empty()) + { + error = _("Transaction must have at least one recipient"); + return false; + } + + CMutableTransaction txNew; + FeeCalculation feeCalc; + TxSize tx_sizes; + int nBytes; + { + std::set<CInputCoin> setCoins; + LOCK(cs_wallet); + txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight()); + { + std::vector<COutput> vAvailableCoins; + AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0); + CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy + coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; + + // Create change script that will be used if we need change + // TODO: pass in scriptChange instead of reservedest so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (!std::get_if<CNoDestination>(&coin_control.destChange)) { + scriptChange = GetScriptForDestination(coin_control.destChange); + } else { // no coin control: send change to newly generated address + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool. If it fails, provide a dummy + // destination in case we don't need change. + CTxDestination dest; + if (!reservedest.GetReservedDestination(dest, true)) { + error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first."); + } + scriptChange = GetScriptForDestination(dest); + // A valid destination implies a change script (and + // vice-versa). An empty change script will abort later, if the + // change keypool ran out, but change is required. + CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty()); + } + CTxOut change_prototype_txout(0, scriptChange); + coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); + + // Get size of spending the change output + int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this); + // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh + // as lower-bound to allow BnB to do it's thing + if (change_spend_size == -1) { + coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE; + } else { + coin_selection_params.change_spend_size = (size_t)change_spend_size; + } + + // Set discard feerate + coin_selection_params.m_discard_feerate = GetDiscardRate(*this); + + // Get the fee rate to use effective values in coin selection + coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc); + // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly + // provided one + if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) { + error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB)); + return false; + } + if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) { + // eventually allow a fallback fee + error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); + return false; + } + + // Get long term estimate + CCoinControl cc_temp; + cc_temp.m_confirm_target = chain().estimateMaxBlocks(); + coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr); + + // Calculate the cost of change + // Cost of change is the cost of creating the change output + cost of spending the change output in the future. + // For creating the change output now, we use the effective feerate. + // For spending the change output in the future, we use the discard feerate for now. + // So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate) + coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size); + coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee; + + coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values + + // vouts to the payees + if (!coin_selection_params.m_subtract_fee_outputs) { + coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size) + } + for (const auto& recipient : vecSend) + { + CTxOut txout(recipient.nAmount, recipient.scriptPubKey); + + // Include the fee cost for outputs. + if (!coin_selection_params.m_subtract_fee_outputs) { + coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION); + } + + if (IsDust(txout, chain().relayDustFee())) + { + error = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + + // Include the fees for things that aren't inputs, excluding the change output + const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size); + CAmount nValueToSelect = nValue + not_input_fees; + + // Choose coins to use + CAmount inputs_sum = 0; + setCoins.clear(); + if (!SelectCoins(vAvailableCoins, /* nTargetValue */ nValueToSelect, setCoins, inputs_sum, coin_control, coin_selection_params)) + { + error = _("Insufficient funds"); + return false; + } + + // Always make a change output + // We will reduce the fee from this change output later, and remove the output if it is too small. + const CAmount change_and_fee = inputs_sum - nValue; + assert(change_and_fee >= 0); + CTxOut newTxOut(change_and_fee, scriptChange); + + if (nChangePosInOut == -1) + { + // Insert change txn at random position: + nChangePosInOut = GetRandInt(txNew.vout.size()+1); + } + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) + { + error = _("Change index out of range"); + return false; + } + + assert(nChangePosInOut != -1); + auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut); + + // Dummy fill vin for maximum size estimation + // + for (const auto& coin : setCoins) { + txNew.vin.push_back(CTxIn(coin.outpoint,CScript())); + } + + // Calculate the transaction fee + tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); + nBytes = tx_sizes.vsize; + if (nBytes < 0) { + error = _("Signing transaction failed"); + return false; + } + nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes); + + // Subtract fee from the change output if not subtrating it from recipient outputs + CAmount fee_needed = nFeeRet; + if (nSubtractFeeFromAmount == 0) { + change_position->nValue -= fee_needed; + } + + // We want to drop the change to fees if: + // 1. The change output would be dust + // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change) + CAmount change_amount = change_position->nValue; + if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change) + { + nChangePosInOut = -1; + change_amount = 0; + txNew.vout.erase(change_position); + + // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those + tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); + nBytes = tx_sizes.vsize; + fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes); + } + + // Update nFeeRet in case fee_needed changed due to dropping the change output + if (fee_needed <= change_and_fee - change_amount) { + nFeeRet = change_and_fee - change_amount; + } + + // Reduce output values for subtractFeeFromAmount + if (nSubtractFeeFromAmount != 0) { + CAmount to_reduce = fee_needed + change_amount - change_and_fee; + int i = 0; + bool fFirst = true; + for (const auto& recipient : vecSend) + { + if (i == nChangePosInOut) { + ++i; + } + CTxOut& txout = txNew.vout[i]; + + if (recipient.fSubtractFeeFromAmount) + { + txout.nValue -= to_reduce / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + + if (fFirst) // first receiver pays the remainder not divisible by output count + { + fFirst = false; + txout.nValue -= to_reduce % nSubtractFeeFromAmount; + } + + // Error if this output is reduced to be below dust + if (IsDust(txout, chain().relayDustFee())) { + if (txout.nValue < 0) { + error = _("The transaction amount is too small to pay the fee"); + } else { + error = _("The transaction amount is too small to send after the fee has been deducted"); + } + return false; + } + } + ++i; + } + nFeeRet = fee_needed; + } + + // Give up if change keypool ran out and change is required + if (scriptChange.empty() && nChangePosInOut != -1) { + return false; + } + } + + // Shuffle selected coins and fill in final vin + txNew.vin.clear(); + std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end()); + Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext()); + + // Note how the sequence number is set to non-maxint so that + // the nLockTime set above actually works. + // + // BIP125 defines opt-in RBF as any nSequence < maxint-1, so + // we use the highest possible value in that range (maxint-2) + // to avoid conflicting with other possible uses of nSequence, + // and in the spirit of "smallest possible change from prior + // behavior." + const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); + for (const auto& coin : selected_coins) { + txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); + } + + if (sign && !SignTransaction(txNew)) { + error = _("Signing transaction failed"); + return false; + } + + // Return the constructed transaction data. + tx = MakeTransactionRef(std::move(txNew)); + + // Limit size + if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) || + (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT)) + { + error = _("Transaction too large"); + return false; + } + } + + if (nFeeRet > m_default_max_tx_fee) { + error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED); + return false; + } + + if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // Lastly, ensure this tx will pass the mempool's chain limits + if (!chain().checkChainLimits(tx)) { + error = _("Transaction has too long of a mempool chain"); + return false; + } + } + + // Before we return success, we assume any change key will be used to prevent + // accidental re-use. + reservedest.KeepDestination(); + fee_calc_out = feeCalc; + + WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", + nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, + feeCalc.est.pass.start, feeCalc.est.pass.end, + (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0, + feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, + feeCalc.est.fail.start, feeCalc.est.fail.end, + (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0, + feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool); + return true; +} + +bool CWallet::CreateTransaction( + const std::vector<CRecipient>& vecSend, + CTransactionRef& tx, + CAmount& nFeeRet, + int& nChangePosInOut, + bilingual_str& error, + const CCoinControl& coin_control, + FeeCalculation& fee_calc_out, + bool sign) +{ + int nChangePosIn = nChangePosInOut; + Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr) + bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign); + // try with avoidpartialspends unless it's enabled already + if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) { + CCoinControl tmp_cc = coin_control; + tmp_cc.m_avoid_partial_spends = true; + CAmount nFeeRet2; + CTransactionRef tx2; + int nChangePosInOut2 = nChangePosIn; + bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results + if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) { + // if fee of this alternative one is within the range of the max fee, we use this one + const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee; + WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped"); + if (use_aps) { + tx = tx2; + nFeeRet = nFeeRet2; + nChangePosInOut = nChangePosInOut2; + } + } + } + return res; +} + +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl) +{ + std::vector<CRecipient> vecSend; + + // Turn the txout set into a CRecipient vector. + for (size_t idx = 0; idx < tx.vout.size(); idx++) { + const CTxOut& txOut = tx.vout[idx]; + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; + vecSend.push_back(recipient); + } + + coinControl.fAllowOtherInputs = true; + + for (const CTxIn& txin : tx.vin) { + coinControl.Select(txin.prevout); + } + + // Acquire the locks to prevent races to the new locked unspents between the + // CreateTransaction call and LockCoin calls (when lockUnspents is true). + LOCK(cs_wallet); + + CTransactionRef tx_new; + FeeCalculation fee_calc_out; + if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) { + return false; + } + + if (nChangePosInOut != -1) { + tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]); + } + + // Copy output sizes from new transaction; they may have had the fee + // subtracted from them. + for (unsigned int idx = 0; idx < tx.vout.size(); idx++) { + tx.vout[idx].nValue = tx_new->vout[idx].nValue; + } + + // Add new txins while keeping original txin scriptSig/order. + for (const CTxIn& txin : tx_new->vin) { + if (!coinControl.IsSelected(txin.prevout)) { + tx.vin.push_back(txin); + + } + if (lockUnspents) { + LockCoin(txin.prevout); + } + + } + + return true; +} diff --git a/src/wallet/spend.h b/src/wallet/spend.h new file mode 100644 index 0000000000..03f9a7c2b5 --- /dev/null +++ b/src/wallet/spend.h @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_SPEND_H +#define BITCOIN_WALLET_SPEND_H + +#include <wallet/coinselection.h> +#include <wallet/transaction.h> +#include <wallet/wallet.h> + +class COutput +{ +public: + const CWalletTx *tx; + + /** Index in tx->vout. */ + int i; + + /** + * Depth in block chain. + * If > 0: the tx is on chain and has this many confirmations. + * If = 0: the tx is waiting confirmation. + * If < 0: a conflicting tx is on chain and has this many confirmations. */ + int nDepth; + + /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */ + int nInputBytes; + + /** Whether we have the private keys to spend this output */ + bool fSpendable; + + /** Whether we know how to spend this output, ignoring the lack of keys */ + bool fSolvable; + + /** Whether to use the maximum sized, 72 byte signature when calculating the size of the input spend. This should only be set when watch-only outputs are allowed */ + bool use_max_sig; + + /** + * Whether this output is considered safe to spend. Unconfirmed transactions + * from outside keys and unconfirmed replacement transactions are considered + * unsafe and will not be used to fund new spending transactions. + */ + bool fSafe; + + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false) + { + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in; + // If known and signable by the given wallet, compute nInputBytes + // Failure will keep this value -1 + if (fSpendable && tx) { + nInputBytes = tx->GetSpendSize(i, use_max_sig); + } + } + + std::string ToString() const; + + inline CInputCoin GetInputCoin() const + { + return CInputCoin(tx->tx, i, nInputBytes); + } +}; + +#endif // BITCOIN_WALLET_SPEND_H diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 7bca385deb..14c3578473 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -35,7 +35,7 @@ static CAmount balance = 0; CoinEligibilityFilter filter_standard(1, 6, 0); CoinEligibilityFilter filter_confirmed(1, 1, 0); CoinEligibilityFilter filter_standard_extra(6, 6, 0); -CoinSelectionParams coin_selection_params(/* use_bnb= */ false, /* change_output_size= */ 0, +CoinSelectionParams coin_selection_params(/* change_output_size= */ 0, /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0), /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); @@ -148,14 +148,13 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) CoinSet selection; CoinSet actual_selection; CAmount value_ret = 0; - CAmount not_input_fees = 0; ///////////////////////// // Known Outcome tests // ///////////////////////// // Empty utxo pool - BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret)); selection.clear(); // Add utxos @@ -166,7 +165,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) // Select 1 Cent add_coin(1 * CENT, 1, actual_selection); - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret)); BOOST_CHECK(equal_sets(selection, actual_selection)); BOOST_CHECK_EQUAL(value_ret, 1 * CENT); actual_selection.clear(); @@ -174,7 +173,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) // Select 2 Cent add_coin(2 * CENT, 2, actual_selection); - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT, selection, value_ret)); BOOST_CHECK(equal_sets(selection, actual_selection)); BOOST_CHECK_EQUAL(value_ret, 2 * CENT); actual_selection.clear(); @@ -183,27 +182,27 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) // Select 5 Cent add_coin(4 * CENT, 4, actual_selection); add_coin(1 * CENT, 1, actual_selection); - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT, selection, value_ret)); BOOST_CHECK(equal_sets(selection, actual_selection)); BOOST_CHECK_EQUAL(value_ret, 5 * CENT); actual_selection.clear(); selection.clear(); // Select 11 Cent, not possible - BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT, selection, value_ret)); actual_selection.clear(); selection.clear(); // Cost of change is greater than the difference between target value and utxo sum add_coin(1 * CENT, 1, actual_selection); - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT, selection, value_ret)); BOOST_CHECK_EQUAL(value_ret, 1 * CENT); BOOST_CHECK(equal_sets(selection, actual_selection)); actual_selection.clear(); selection.clear(); // Cost of change is less than the difference between target value and utxo sum - BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0, selection, value_ret, not_input_fees)); + BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0, selection, value_ret)); actual_selection.clear(); selection.clear(); @@ -212,7 +211,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(5 * CENT, 5, actual_selection); add_coin(4 * CENT, 4, actual_selection); add_coin(1 * CENT, 1, actual_selection); - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT, selection, value_ret)); BOOST_CHECK(equal_sets(selection, actual_selection)); BOOST_CHECK_EQUAL(value_ret, 10 * CENT); actual_selection.clear(); @@ -223,21 +222,21 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(5 * CENT, 5, actual_selection); add_coin(3 * CENT, 3, actual_selection); add_coin(2 * CENT, 2, actual_selection); - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000, selection, value_ret, not_input_fees)); + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000, selection, value_ret)); BOOST_CHECK_EQUAL(value_ret, 10 * CENT); // FIXME: this test is redundant with the above, because 1 Cent is selected, not "too small" // BOOST_CHECK(equal_sets(selection, actual_selection)); // Select 0.25 Cent, not possible - BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT, selection, value_ret)); actual_selection.clear(); selection.clear(); // Iteration exhaustion test CAmount target = make_hard_case(17, utxo_pool); - BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret, not_input_fees)); // Should exhaust + BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret)); // Should exhaust target = make_hard_case(14, utxo_pool); - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret, not_input_fees)); // Should not exhaust + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret)); // Should not exhaust // Test same value early bailout optimization utxo_pool.clear(); @@ -254,7 +253,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) for (int i = 0; i < 50000; ++i) { add_coin(5 * CENT, 7, utxo_pool); } - BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000, selection, value_ret, not_input_fees)); + BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000, selection, value_ret)); BOOST_CHECK_EQUAL(value_ret, 30 * CENT); BOOST_CHECK(equal_sets(selection, actual_selection)); @@ -268,29 +267,27 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) } // Run 100 times, to make sure it is never finding a solution for (int i = 0; i < 100; ++i) { - BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT, selection, value_ret, not_input_fees)); + BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT, selection, value_ret)); } // Make sure that effective value is working in SelectCoinsMinConf when BnB is used - CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 0, + CoinSelectionParams coin_selection_params_bnb(/* change_output_size= */ 0, /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000), /* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000), /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); CoinSet setCoinsRet; CAmount nValueRet; - bool bnb_used; empty_wallet(); add_coin(1); vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb)); // Test fees subtracted from output: empty_wallet(); add_coin(1 * CENT); vCoins.at(0).nInputBytes = 40; - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); coin_selection_params_bnb.m_subtract_fee_outputs = true; - BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // Make sure that can use BnB when there are preset inputs @@ -307,9 +304,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) coin_control.fAllowOtherInputs = true; coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i)); coin_selection_params_bnb.m_effective_feerate = CFeeRate(0); - BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used)); - BOOST_CHECK(bnb_used); - BOOST_CHECK(coin_selection_params_bnb.use_bnb); + BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb)); } } @@ -317,7 +312,6 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) { CoinSet setCoinsRet, setCoinsRet2; CAmount nValueRet; - bool bnb_used; LOCK(testWallet.cs_wallet); testWallet.SetupLegacyScriptPubKeyMan(); @@ -328,24 +322,24 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) empty_wallet(); // with an empty wallet we can't even pay one cent - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params)); add_coin(1*CENT, 4); // add a new 1 cent coin // with a new 1 cent coin, we still can't find a mature 1 cent - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params)); // but we can find a new 1 cent - BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); add_coin(2*CENT); // add a mature 2 cent coin // we can't make 3 cents of mature coins - BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params)); // we can make 3 cents of new coins - BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 3 * CENT); add_coin(5*CENT); // add a mature 5 cent coin, @@ -355,33 +349,33 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38 // we can't make 38 cents only if we disallow new coins: - BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params)); // we can't even make 37 cents if we don't allow new coins even if they're from us - BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params)); // but we can make 37 cents if we accept new coins from ourself - BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 37 * CENT); // and we can make 38 cents if we accept all new coins - BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 38 * CENT); // try making 34 cents from 1,2,5,10,20 - we can't do it exactly - BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible) // when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5 - BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 7 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough. - BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK(nValueRet == 8 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10) - BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 10 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -395,30 +389,30 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total // check that we have 71 and not 72 - BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); + BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); // now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20 - BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20 - BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30 // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18 - BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins // now try making 11 cents. we should get 5+6 - BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 11 * CENT); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -427,11 +421,11 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin( 2*COIN); add_coin( 3*COIN); add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents - BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); - BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -446,14 +440,14 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly - BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // but if we add a bigger coin, small change is avoided add_coin(1111*MIN_CHANGE); // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 - BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount // if we add more small coins: @@ -461,7 +455,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 7 / 10); // and try again to make 1.0 * MIN_CHANGE - BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount // run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf) @@ -470,7 +464,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) for (int j = 0; j < 20; j++) add_coin(50000 * COIN); - BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins @@ -483,7 +477,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 6 / 10); add_coin(MIN_CHANGE * 7 / 10); add_coin(1111 * MIN_CHANGE); - BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -493,7 +487,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 6 / 10); add_coin(MIN_CHANGE * 8 / 10); add_coin(1111 * MIN_CHANGE); - BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6 @@ -504,12 +498,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) add_coin(MIN_CHANGE * 100); // trying to make 100.01 from these three coins - BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change - BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); } @@ -523,7 +517,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times. for (int i = 0; i < RUN_TESTS; i++) { - BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params)); if (amt - 2000 < MIN_CHANGE) { // needs more than one input: @@ -549,17 +543,19 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) for (int i = 0; i < RUN_TESTS; i++) { // picking 50 from 100 coins doesn't depend on the shuffle, // but does depend on randomness in the stochastic approximation code - BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet, nValueRet)); + BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet)); BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2)); int fails = 0; for (int j = 0; j < RANDOM_REPEATS; j++) { - // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time - // run the test RANDOM_REPEATS times and only complain if all of them fail - BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); + // Test that the KnapsackSolver selects randomly from equivalent coins (same value and same input size). + // When choosing 1 from 100 identical coins, 1% of the time, this test will choose the same coin twice + // which will cause it to fail. + // To avoid that issue, run the test RANDOM_REPEATS times and only complain if all of them fail + BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet, nValueRet)); + BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet)); if (equal_sets(setCoinsRet, setCoinsRet2)) fails++; } @@ -579,10 +575,8 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) int fails = 0; for (int j = 0; j < RANDOM_REPEATS; j++) { - // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time - // run the test RANDOM_REPEATS times and only complain if all of them fail - BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet , nValueRet, coin_selection_params, bnb_used)); - BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, filter_standard, vCoins, setCoinsRet2, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet, nValueRet)); + BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet2, nValueRet)); if (equal_sets(setCoinsRet, setCoinsRet2)) fails++; } @@ -597,7 +591,6 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) { CoinSet setCoinsRet; CAmount nValueRet; - bool bnb_used; LOCK(testWallet.cs_wallet); testWallet.SetupLegacyScriptPubKeyMan(); @@ -609,7 +602,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) add_coin(1000 * COIN); add_coin(3 * COIN); - BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params)); BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -619,6 +612,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) // Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value BOOST_AUTO_TEST_CASE(SelectCoins_test) { + LOCK(testWallet.cs_wallet); testWallet.SetupLegacyScriptPubKeyMan(); // Random generator stuff @@ -644,19 +638,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test) CAmount target = rand.randrange(balance - 1000) + 1000; // Perform selection - CoinSelectionParams coin_selection_params_knapsack(/* use_bnb= */ false, /* change_output_size= */ 34, - /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0), - /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), - /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); - CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 34, - /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0), - /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), - /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); + CoinSelectionParams cs_params(/* change_output_size= */ 34, + /* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0), + /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0), + /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false); CoinSet out_set; CAmount out_value = 0; - bool bnb_used = false; - BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_bnb, bnb_used) || - testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_knapsack, bnb_used)); + CCoinControl cc; + BOOST_CHECK(testWallet.SelectCoins(vCoins, target, out_set, out_value, cc, cs_params)); BOOST_CHECK_GE(out_value, target); } } diff --git a/src/wallet/transaction.cpp b/src/wallet/transaction.cpp new file mode 100644 index 0000000000..cf98b516f1 --- /dev/null +++ b/src/wallet/transaction.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <wallet/transaction.h> + +bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const +{ + CMutableTransaction tx1 {*this->tx}; + CMutableTransaction tx2 {*_tx.tx}; + for (auto& txin : tx1.vin) txin.scriptSig = CScript(); + for (auto& txin : tx2.vin) txin.scriptSig = CScript(); + return CTransaction(tx1) == CTransaction(tx2); +} + +bool CWalletTx::InMempool() const +{ + return fInMempool; +} + +int64_t CWalletTx::GetTxTime() const +{ + int64_t n = nTimeSmart; + return n ? n : nTimeReceived; +} diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h new file mode 100644 index 0000000000..b57ab3db48 --- /dev/null +++ b/src/wallet/transaction.h @@ -0,0 +1,358 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_TRANSACTION_H +#define BITCOIN_WALLET_TRANSACTION_H + +#include <amount.h> +#include <primitives/transaction.h> +#include <serialize.h> +#include <wallet/ismine.h> +#include <threadsafety.h> +#include <tinyformat.h> +#include <util/strencodings.h> +#include <util/string.h> + +#include <list> +#include <vector> + +struct COutputEntry; + +typedef std::map<std::string, std::string> mapValue_t; + +//Get the marginal bytes of spending the specified output +int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false); + +static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (!mapValue.count("n")) + { + nOrderPos = -1; // TODO: calculate elsewhere + return; + } + nOrderPos = atoi64(mapValue["n"]); +} + +static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (nOrderPos == -1) + return; + mapValue["n"] = ToString(nOrderPos); +} + +/** Legacy class used for deserializing vtxPrev for backwards compatibility. + * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3, + * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs. + * These need to get deserialized for field alignment when deserializing + * a CWalletTx, but the deserialized values are discarded.**/ +class CMerkleTx +{ +public: + template<typename Stream> + void Unserialize(Stream& s) + { + CTransactionRef tx; + uint256 hashBlock; + std::vector<uint256> vMerkleBranch; + int nIndex; + + s >> tx >> hashBlock >> vMerkleBranch >> nIndex; + } +}; + +/** + * A transaction with a bunch of additional info that only the owner cares about. + * It includes any unrecorded transactions needed to link it back to the block chain. + */ +class CWalletTx +{ +private: + const CWallet* const pwallet; + + /** Constant used in hashBlock to indicate tx has been abandoned, only used at + * serialization/deserialization to avoid ambiguity with conflicted. + */ + static constexpr const uint256& ABANDON_HASH = uint256::ONE; + +public: + /** + * Key/value map with information about the transaction. + * + * The following keys can be read and written through the map and are + * serialized in the wallet database: + * + * "comment", "to" - comment strings provided to sendtoaddress, + * and sendmany wallet RPCs + * "replaces_txid" - txid (as HexStr) of transaction replaced by + * bumpfee on transaction created by bumpfee + * "replaced_by_txid" - txid (as HexStr) of transaction created by + * bumpfee on transaction replaced by bumpfee + * "from", "message" - obsolete fields that could be set in UI prior to + * 2011 (removed in commit 4d9b223) + * + * The following keys are serialized in the wallet database, but shouldn't + * be read or written through the map (they will be temporarily added and + * removed from the map during serialization): + * + * "fromaccount" - serialized strFromAccount value + * "n" - serialized nOrderPos value + * "timesmart" - serialized nTimeSmart value + * "spent" - serialized vfSpent value that existed prior to + * 2014 (removed in commit 93a18a3) + */ + mapValue_t mapValue; + std::vector<std::pair<std::string, std::string> > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; //!< time received by this node + /** + * Stable timestamp that never changes, and reflects the order a transaction + * was added to the wallet. Timestamp is based on the block time for a + * transaction added as part of a block, or else the time when the + * transaction was received if it wasn't part of a block, with the timestamp + * adjusted in both cases so timestamp order matches the order transactions + * were added to the wallet. More details can be found in + * CWallet::ComputeTimeSmart(). + */ + unsigned int nTimeSmart; + /** + * From me flag is set to 1 for transactions that were created by the wallet + * on this bitcoin node, and set to 0 for transactions that were created + * externally and came in through the network or sendrawtransaction RPC. + */ + bool fFromMe; + int64_t nOrderPos; //!< position in ordered transaction list + std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered; + + // memory only + enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS }; + CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const; + mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS]; + /** + * This flag is true if all m_amounts caches are empty. This is particularly + * useful in places where MarkDirty is conditionally called and the + * condition can be expensive and thus can be skipped if the flag is true. + * See MarkDestinationsDirty. + */ + mutable bool m_is_cache_empty{true}; + mutable bool fChangeCached; + mutable bool fInMempool; + mutable CAmount nChangeCached; + + CWalletTx(const CWallet* wallet, CTransactionRef arg) + : pwallet(wallet), + tx(std::move(arg)) + { + Init(); + } + + void Init() + { + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + nTimeSmart = 0; + fFromMe = false; + fChangeCached = false; + fInMempool = false; + nChangeCached = 0; + nOrderPos = -1; + m_confirm = Confirmation{}; + } + + CTransactionRef tx; + + /** New transactions start as UNCONFIRMED. At BlockConnected, + * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected, + * they roll back to UNCONFIRMED. If we detect a conflicting transaction at + * block connection, we update conflicted tx and its dependencies as CONFLICTED. + * If tx isn't confirmed and outside of mempool, the user may switch it to ABANDONED + * by using the abandontransaction call. This last status may be override by a CONFLICTED + * or CONFIRMED transition. + */ + enum Status { + UNCONFIRMED, + CONFIRMED, + CONFLICTED, + ABANDONED + }; + + /** Confirmation includes tx status and a triplet of {block height/block hash/tx index in block} + * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned. + * Meaning of these fields changes with CONFLICTED state where they instead point to block hash + * and block height of the deepest conflicting tx. + */ + struct Confirmation { + Status status; + int block_height; + uint256 hashBlock; + int nIndex; + Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {} + }; + + Confirmation m_confirm; + + template<typename Stream> + void Serialize(Stream& s) const + { + mapValue_t mapValueCopy = mapValue; + + mapValueCopy["fromaccount"] = ""; + WriteOrderPos(nOrderPos, mapValueCopy); + if (nTimeSmart) { + mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart); + } + + std::vector<char> dummy_vector1; //!< Used to be vMerkleBranch + std::vector<char> dummy_vector2; //!< Used to be vtxPrev + bool dummy_bool = false; //!< Used to be fSpent + uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock; + int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex; + s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool; + } + + template<typename Stream> + void Unserialize(Stream& s) + { + Init(); + + std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch + std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev + bool dummy_bool; //! Used to be fSpent + int serializedIndex; + s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool; + + /* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to + * the earliest block in the chain we know this or any in-wallet ancestor conflicts + * with. If nIndex == -1 and hashBlock is ABANDON_HASH, it means transaction is abandoned. + * In same context, an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) or + * unconfirmed one. Older clients interpret nIndex == -1 as unconfirmed for backward + * compatibility (pre-commit 9ac63d6). + */ + if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) { + setAbandoned(); + } else if (serializedIndex == -1) { + setConflicted(); + } else if (!m_confirm.hashBlock.IsNull()) { + m_confirm.nIndex = serializedIndex; + setConfirmed(); + } + + ReadOrderPos(nOrderPos, mapValue); + nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; + + mapValue.erase("fromaccount"); + mapValue.erase("spent"); + mapValue.erase("n"); + mapValue.erase("timesmart"); + } + + void SetTx(CTransactionRef arg) + { + tx = std::move(arg); + } + + //! make sure balances are recalculated + void MarkDirty() + { + m_amounts[DEBIT].Reset(); + m_amounts[CREDIT].Reset(); + m_amounts[IMMATURE_CREDIT].Reset(); + m_amounts[AVAILABLE_CREDIT].Reset(); + fChangeCached = false; + m_is_cache_empty = true; + } + + //! filter decides which addresses will count towards the debit + CAmount GetDebit(const isminefilter& filter) const; + CAmount GetCredit(const isminefilter& filter) const; + CAmount GetImmatureCredit(bool fUseCache = true) const; + // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct + // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The + // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid + // having to resolve the issue of member access into incomplete type CWallet. + CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS; + CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const; + CAmount GetChange() const; + + /** Get the marginal bytes if spending the specified output from this transaction */ + int GetSpendSize(unsigned int out, bool use_max_sig = false) const + { + return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig); + } + + void GetAmounts(std::list<COutputEntry>& listReceived, + std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const; + + bool IsFromMe(const isminefilter& filter) const + { + return (GetDebit(filter) > 0); + } + + /** True if only scriptSigs are different */ + bool IsEquivalentTo(const CWalletTx& tx) const; + + bool InMempool() const; + bool IsTrusted() const; + + int64_t GetTxTime() const; + + /** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */ + bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay); + + // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct + // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation + // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to + // resolve the issue of member access into incomplete type CWallet. Note + // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)" + // in place. + std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS; + + /** + * Return depth of transaction in blockchain: + * <0 : conflicts with a transaction this deep in the blockchain + * 0 : in memory pool, waiting to be included in a block + * >=1 : this many blocks deep in the main chain + */ + // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct + // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation + // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to + // resolve the issue of member access into incomplete type CWallet. Note + // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)" + // in place. + int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS; + bool IsInMainChain() const { return GetDepthInMainChain() > 0; } + + /** + * @return number of blocks to maturity for this transaction: + * 0 : is not a coinbase transaction, or is a mature coinbase transaction + * >0 : is a coinbase transaction which matures in this many blocks + */ + int GetBlocksToMaturity() const; + bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; } + void setAbandoned() + { + m_confirm.status = CWalletTx::ABANDONED; + m_confirm.hashBlock = uint256(); + m_confirm.block_height = 0; + m_confirm.nIndex = 0; + } + bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; } + void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; } + bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; } + void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; } + bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; } + void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; } + const uint256& GetHash() const { return tx->GetHash(); } + bool IsCoinBase() const { return tx->IsCoinBase(); } + bool IsImmatureCoinBase() const; + + // Disable copying of CWalletTx objects to prevent bugs where instances get + // copied in and out of the mapWallet map, and fields are updated in the + // wrong copy. + CWalletTx(CWalletTx const &) = delete; + void operator=(CWalletTx const &x) = delete; +}; + +#endif // BITCOIN_WALLET_TRANSACTION_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7cdf2fcda0..9986a02fc1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -53,8 +53,6 @@ const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{ }, }; -static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10; - RecursiveMutex cs_wallets; static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets); static std::list<LoadWalletFn> g_load_wallet_fns GUARDED_BY(cs_wallets); @@ -213,7 +211,7 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std: return nullptr; } - chain.initMessage(_("Loading wallet...").translated); + chain.initMessage(_("Loading wallet…").translated); std::shared_ptr<CWallet> wallet = CWallet::Create(&chain, name, std::move(database), options.create_flags, error, warnings); if (!wallet) { error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error; @@ -293,7 +291,7 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin } // Make the wallet - chain.initMessage(_("Loading wallet...").translated); + chain.initMessage(_("Loading wallet…").translated); std::shared_ptr<CWallet> wallet = CWallet::Create(&chain, name, std::move(database), wallet_creation_flags, error, warnings); if (!wallet) { error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error; @@ -351,11 +349,6 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin * @{ */ -std::string COutput::ToString() const -{ - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); -} - const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const { AssertLockHeld(cs_wallet); @@ -1283,20 +1276,6 @@ void CWallet::BlockUntilSyncedToCurrentChain() const { chain().waitForNotificationsIfTipChanged(last_block_hash); } - -isminetype CWallet::IsMine(const CTxIn &txin) const -{ - AssertLockHeld(cs_wallet); - std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.tx->vout.size()) - return IsMine(prev.tx->vout[txin.prevout.n]); - } - return ISMINE_NO; -} - // Note that this function doesn't distinguish between a 0-valued input, // and a not-"is mine" (according to the filter) input. CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const @@ -1337,49 +1316,6 @@ isminetype CWallet::IsMine(const CScript& script) const return result; } -CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const -{ - if (!MoneyRange(txout.nValue)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - LOCK(cs_wallet); - return ((IsMine(txout) & filter) ? txout.nValue : 0); -} - -bool CWallet::IsChange(const CTxOut& txout) const -{ - return IsChange(txout.scriptPubKey); -} - -bool CWallet::IsChange(const CScript& script) const -{ - // TODO: fix handling of 'change' outputs. The assumption is that any - // payment to a script that is ours, but is not in the address book - // is change. That assumption is likely to break when we implement multisignature - // wallets that return change back into a multi-signature-protected address; - // a better way of identifying which outputs are 'the send' and which are - // 'the change' will need to be implemented (maybe extend CWalletTx to remember - // which output, if any, was change). - AssertLockHeld(cs_wallet); - if (IsMine(script)) - { - CTxDestination address; - if (!ExtractDestination(script, address)) - return true; - if (!FindAddressBookEntry(address)) { - return true; - } - } - return false; -} - -CAmount CWallet::GetChange(const CTxOut& txout) const -{ - AssertLockHeld(cs_wallet); - if (!MoneyRange(txout.nValue)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - return (IsChange(txout) ? txout.nValue : 0); -} - bool CWallet::IsMine(const CTransaction& tx) const { AssertLockHeld(cs_wallet); @@ -1406,52 +1342,6 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co return nDebit; } -bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const -{ - LOCK(cs_wallet); - - for (const CTxIn& txin : tx.vin) - { - auto mi = mapWallet.find(txin.prevout.hash); - if (mi == mapWallet.end()) - return false; // any unknown inputs can't be from us - - const CWalletTx& prev = (*mi).second; - - if (txin.prevout.n >= prev.tx->vout.size()) - return false; // invalid input! - - if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) - return false; - } - return true; -} - -CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const -{ - CAmount nCredit = 0; - for (const CTxOut& txout : tx.vout) - { - nCredit += GetCredit(txout, filter); - if (!MoneyRange(nCredit)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - } - return nCredit; -} - -CAmount CWallet::GetChange(const CTransaction& tx) const -{ - LOCK(cs_wallet); - CAmount nChange = 0; - for (const CTxOut& txout : tx.vout) - { - nChange += GetChange(txout); - if (!MoneyRange(nChange)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - } - return nChange; -} - bool CWallet::IsHDEnabled() const { // All Active ScriptPubKeyMans must be HD for this to be true @@ -1531,12 +1421,6 @@ bool CWallet::AddWalletFlags(uint64_t flags) return LoadWalletFlags(flags); } -int64_t CWalletTx::GetTxTime() const -{ - int64_t n = nTimeSmart; - return n ? n : nTimeReceived; -} - // Helper for producing a max-sized low-S low-R signature (eg 71 bytes) // or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const @@ -1627,101 +1511,6 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri return true; } -// Returns pair of vsize and weight -std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig) -{ - std::vector<CTxOut> txouts; - for (const CTxIn& input : tx.vin) { - const auto mi = wallet->mapWallet.find(input.prevout.hash); - // Can not estimate size without knowing the input details - if (mi == wallet->mapWallet.end()) { - return std::make_pair(-1, -1); - } - assert(input.prevout.n < mi->second.tx->vout.size()); - txouts.emplace_back(mi->second.tx->vout[input.prevout.n]); - } - return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig); -} - -// txouts needs to be in the order of tx.vin -std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig) -{ - CMutableTransaction txNew(tx); - if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) { - return std::make_pair(-1, -1); - } - CTransaction ctx(txNew); - int64_t vsize = GetVirtualTransactionSize(ctx); - int64_t weight = GetTransactionWeight(ctx); - return std::make_pair(vsize, weight); -} - -int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig) -{ - CMutableTransaction txn; - txn.vin.push_back(CTxIn(COutPoint())); - if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) { - return -1; - } - return GetVirtualTransactionInputSize(txn.vin[0]); -} - -void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, - std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const -{ - nFee = 0; - listReceived.clear(); - listSent.clear(); - - // Compute fee: - CAmount nDebit = GetDebit(filter); - if (nDebit > 0) // debit>0 means we signed/sent this transaction - { - CAmount nValueOut = tx->GetValueOut(); - nFee = nDebit - nValueOut; - } - - LOCK(pwallet->cs_wallet); - // Sent/received. - for (unsigned int i = 0; i < tx->vout.size(); ++i) - { - const CTxOut& txout = tx->vout[i]; - isminetype fIsMine = pwallet->IsMine(txout); - // Only need to handle txouts if AT LEAST one of these is true: - // 1) they debit from us (sent) - // 2) the output is to us (received) - if (nDebit > 0) - { - // Don't report 'change' txouts - if (pwallet->IsChange(txout)) - continue; - } - else if (!(fIsMine & filter)) - continue; - - // In either case, we need to get the destination address - CTxDestination address; - - if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable()) - { - pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString()); - address = CNoDestination(); - } - - COutputEntry output = {address, txout.nValue, (int)i}; - - // If we are debited by the transaction, add the output as a "sent" entry - if (nDebit > 0) - listSent.push_back(output); - - // If we are receiving the output, add it as a "received" entry - if (fIsMine & filter) - listReceived.push_back(output); - } - -} - /** * Scan active chain for relevant transactions after importing keys. This should * be called whenever new keys are added to the wallet, with the oldest key @@ -1944,165 +1733,6 @@ std::set<uint256> CWalletTx::GetConflicts() const return result; } -CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const -{ - auto& amount = m_amounts[type]; - if (recalculate || !amount.m_cached[filter]) { - amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter)); - m_is_cache_empty = false; - } - return amount.m_value[filter]; -} - -CAmount CWalletTx::GetDebit(const isminefilter& filter) const -{ - if (tx->vin.empty()) - return 0; - - CAmount debit = 0; - if (filter & ISMINE_SPENDABLE) { - debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE); - } - if (filter & ISMINE_WATCH_ONLY) { - debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY); - } - return debit; -} - -CAmount CWalletTx::GetCredit(const isminefilter& filter) const -{ - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsImmatureCoinBase()) - return 0; - - CAmount credit = 0; - if (filter & ISMINE_SPENDABLE) { - // GetBalance can assume transactions in mapWallet won't change - credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE); - } - if (filter & ISMINE_WATCH_ONLY) { - credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY); - } - return credit; -} - -CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const -{ - if (IsImmatureCoinBase() && IsInMainChain()) { - return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache); - } - - return 0; -} - -CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const -{ - if (pwallet == nullptr) - return 0; - - // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future). - bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL; - - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsImmatureCoinBase()) - return 0; - - if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) { - return m_amounts[AVAILABLE_CREDIT].m_value[filter]; - } - - bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); - CAmount nCredit = 0; - uint256 hashTx = GetHash(); - for (unsigned int i = 0; i < tx->vout.size(); i++) - { - if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) { - const CTxOut &txout = tx->vout[i]; - nCredit += pwallet->GetCredit(txout, filter); - if (!MoneyRange(nCredit)) - throw std::runtime_error(std::string(__func__) + " : value out of range"); - } - } - - if (allow_cache) { - m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit); - m_is_cache_empty = false; - } - - return nCredit; -} - -CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const -{ - if (IsImmatureCoinBase() && IsInMainChain()) { - return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache); - } - - return 0; -} - -CAmount CWalletTx::GetChange() const -{ - if (fChangeCached) - return nChangeCached; - nChangeCached = pwallet->GetChange(*tx); - fChangeCached = true; - return nChangeCached; -} - -bool CWalletTx::InMempool() const -{ - return fInMempool; -} - -bool CWalletTx::IsTrusted() const -{ - std::set<uint256> trusted_parents; - LOCK(pwallet->cs_wallet); - return pwallet->IsTrusted(*this, trusted_parents); -} - -bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const -{ - AssertLockHeld(cs_wallet); - // Quick answer in most cases - if (!chain().checkFinalTx(*wtx.tx)) return false; - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth >= 1) return true; - if (nDepth < 0) return false; - // using wtx's cached debit - if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false; - - // Don't trust unconfirmed transactions from us unless they are in the mempool. - if (!wtx.InMempool()) return false; - - // Trusted if all inputs are from us and are in the mempool: - for (const CTxIn& txin : wtx.tx->vin) - { - // Transactions not sent by us: not trusted - const CWalletTx* parent = GetWalletTx(txin.prevout.hash); - if (parent == nullptr) return false; - const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; - // Check that this specific input being spent is trusted - if (IsMine(parentOut) != ISMINE_SPENDABLE) return false; - // If we've already trusted this parent, continue - if (trusted_parents.count(parent->GetHash())) continue; - // Recurse to check that the parent is also trusted - if (!IsTrusted(*parent, trusted_parents)) return false; - trusted_parents.insert(parent->GetHash()); - } - return true; -} - -bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const -{ - CMutableTransaction tx1 {*this->tx}; - CMutableTransaction tx2 {*_tx.tx}; - for (auto& txin : tx1.vin) txin.scriptSig = CScript(); - for (auto& txin : tx2.vin) txin.scriptSig = CScript(); - return CTransaction(tx1) == CTransaction(tx2); -} - // Rebroadcast transactions from the wallet. We do this on a random timer // to slightly obfuscate which transactions come from our wallet. // @@ -2163,410 +1793,6 @@ void MaybeResendWalletTxs() * @{ */ - -CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const -{ - Balance ret; - isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED; - { - LOCK(cs_wallet); - std::set<uint256> trusted_parents; - for (const auto& entry : mapWallet) - { - const CWalletTx& wtx = entry.second; - const bool is_trusted{IsTrusted(wtx, trusted_parents)}; - const int tx_depth{wtx.GetDepthInMainChain()}; - const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)}; - const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)}; - if (is_trusted && tx_depth >= min_depth) { - ret.m_mine_trusted += tx_credit_mine; - ret.m_watchonly_trusted += tx_credit_watchonly; - } - if (!is_trusted && tx_depth == 0 && wtx.InMempool()) { - ret.m_mine_untrusted_pending += tx_credit_mine; - ret.m_watchonly_untrusted_pending += tx_credit_watchonly; - } - ret.m_mine_immature += wtx.GetImmatureCredit(); - ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(); - } - } - return ret; -} - -CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const -{ - LOCK(cs_wallet); - - CAmount balance = 0; - std::vector<COutput> vCoins; - AvailableCoins(vCoins, coinControl); - for (const COutput& out : vCoins) { - if (out.fSpendable) { - balance += out.tx->tx->vout[out.i].nValue; - } - } - return balance; -} - -void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const -{ - AssertLockHeld(cs_wallet); - - vCoins.clear(); - CAmount nTotal = 0; - // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where - // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses - bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse); - const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH}; - const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH}; - const bool only_safe = {coinControl ? !coinControl->m_include_unsafe_inputs : true}; - - std::set<uint256> trusted_parents; - for (const auto& entry : mapWallet) - { - const uint256& wtxid = entry.first; - const CWalletTx& wtx = entry.second; - - if (!chain().checkFinalTx(*wtx.tx)) { - continue; - } - - if (wtx.IsImmatureCoinBase()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < 0) - continue; - - // We should not consider coins which aren't at least in our mempool - // It's possible for these to be conflicted via ancestors which we may never be able to detect - if (nDepth == 0 && !wtx.InMempool()) - continue; - - bool safeTx = IsTrusted(wtx, trusted_parents); - - // We should not consider coins from transactions that are replacing - // other transactions. - // - // Example: There is a transaction A which is replaced by bumpfee - // transaction B. In this case, we want to prevent creation of - // a transaction B' which spends an output of B. - // - // Reason: If transaction A were initially confirmed, transactions B - // and B' would no longer be valid, so the user would have to create - // a new transaction C to replace B'. However, in the case of a - // one-block reorg, transactions B' and C might BOTH be accepted, - // when the user only wanted one of them. Specifically, there could - // be a 1-block reorg away from the chain where transactions A and C - // were accepted to another chain where B, B', and C were all - // accepted. - if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) { - safeTx = false; - } - - // Similarly, we should not consider coins from transactions that - // have been replaced. In the example above, we would want to prevent - // creation of a transaction A' spending an output of A, because if - // transaction B were initially confirmed, conflicting with A and - // A', we wouldn't want to the user to create a transaction D - // intending to replace A', but potentially resulting in a scenario - // where A, A', and D could all be accepted (instead of just B and - // D, or just A and A' like the user would want). - if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) { - safeTx = false; - } - - if (only_safe && !safeTx) { - continue; - } - - if (nDepth < min_depth || nDepth > max_depth) { - continue; - } - - for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { - // Only consider selected coins if add_inputs is false - if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) { - continue; - } - - if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount) - continue; - - if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) - continue; - - if (IsLockedCoin(entry.first, i)) - continue; - - if (IsSpent(wtxid, i)) - continue; - - isminetype mine = IsMine(wtx.tx->vout[i]); - - if (mine == ISMINE_NO) { - continue; - } - - if (!allow_used_addresses && IsSpentKey(wtxid, i)) { - continue; - } - - std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey); - - bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false; - bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); - - vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly))); - - // Checks the sum amount of all UTXO's. - if (nMinimumSumAmount != MAX_MONEY) { - nTotal += wtx.tx->vout[i].nValue; - - if (nTotal >= nMinimumSumAmount) { - return; - } - } - - // Checks the maximum number of UTXO's. - if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { - return; - } - } - } -} - -std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const -{ - AssertLockHeld(cs_wallet); - - std::map<CTxDestination, std::vector<COutput>> result; - std::vector<COutput> availableCoins; - - AvailableCoins(availableCoins); - - for (const COutput& coin : availableCoins) { - CTxDestination address; - if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) && - ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) { - result[address].emplace_back(std::move(coin)); - } - } - - std::vector<COutPoint> lockedCoins; - ListLockedCoins(lockedCoins); - // Include watch-only for LegacyScriptPubKeyMan wallets without private keys - const bool include_watch_only = GetLegacyScriptPubKeyMan() && IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); - const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; - for (const COutPoint& output : lockedCoins) { - auto it = mapWallet.find(output.hash); - if (it != mapWallet.end()) { - int depth = it->second.GetDepthInMainChain(); - if (depth >= 0 && output.n < it->second.tx->vout.size() && - IsMine(it->second.tx->vout[output.n]) == is_mine_filter - ) { - CTxDestination address; - if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) { - result[address].emplace_back( - &it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */); - } - } - } - } - - return result; -} - -const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const -{ - AssertLockHeld(cs_wallet); - const CTransaction* ptx = &tx; - int n = output; - while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) { - const COutPoint& prevout = ptx->vin[0].prevout; - auto it = mapWallet.find(prevout.hash); - if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n || - !IsMine(it->second.tx->vout[prevout.n])) { - break; - } - ptx = it->second.tx.get(); - n = prevout.n; - } - return ptx->vout[n]; -} - -bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins, - std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const -{ - setCoinsRet.clear(); - nValueRet = 0; - - if (coin_selection_params.use_bnb) { - // Get the feerate for effective value. - // When subtracting the fee from the outputs, we want the effective feerate to be 0 - CFeeRate effective_feerate{0}; - if (!coin_selection_params.m_subtract_fee_outputs) { - effective_feerate = coin_selection_params.m_effective_feerate; - } - - std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, coin_selection_params.m_long_term_feerate, eligibility_filter, true /* positive_only */); - - // Calculate cost of change - CAmount cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size); - - // Calculate the fees for things that aren't inputs - CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size); - bnb_used = true; - return SelectCoinsBnB(groups, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees); - } else { - std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, CFeeRate(0), CFeeRate(0), eligibility_filter, false /* positive_only */); - - bnb_used = false; - return KnapsackSolver(nTargetValue, groups, setCoinsRet, nValueRet); - } -} - -bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const -{ - std::vector<COutput> vCoins(vAvailableCoins); - CAmount value_to_select = nTargetValue; - - // Default to bnb was not used. If we use it, we set it later - bnb_used = false; - - // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) - if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs) - { - for (const COutput& out : vCoins) - { - if (!out.fSpendable) - continue; - nValueRet += out.tx->tx->vout[out.i].nValue; - setCoinsRet.insert(out.GetInputCoin()); - } - return (nValueRet >= nTargetValue); - } - - // calculate value from preset inputs and store them - std::set<CInputCoin> setPresetCoins; - CAmount nValueFromPresetInputs = 0; - - std::vector<COutPoint> vPresetInputs; - coin_control.ListSelected(vPresetInputs); - for (const COutPoint& outpoint : vPresetInputs) - { - std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); - if (it != mapWallet.end()) - { - const CWalletTx& wtx = it->second; - // Clearly invalid input, fail - if (wtx.tx->vout.size() <= outpoint.n) { - return false; - } - // Just to calculate the marginal byte size - CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false)); - nValueFromPresetInputs += coin.txout.nValue; - if (coin.m_input_bytes <= 0) { - return false; // Not solvable, can't estimate size for fee - } - coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes); - if (coin_selection_params.use_bnb) { - value_to_select -= coin.effective_value; - } else { - value_to_select -= coin.txout.nValue; - } - setPresetCoins.insert(coin); - } else { - return false; // TODO: Allow non-wallet inputs - } - } - - // remove preset inputs from vCoins so that Coin Selection doesn't pick them. - for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();) - { - if (setPresetCoins.count(it->GetInputCoin())) - it = vCoins.erase(it); - else - ++it; - } - - unsigned int limit_ancestor_count = 0; - unsigned int limit_descendant_count = 0; - chain().getPackageLimits(limit_ancestor_count, limit_descendant_count); - const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count); - const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count); - const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); - - // form groups from remaining coins; note that preset coins will not - // automatically have their associated (same address) coins included - if (coin_control.m_avoid_partial_spends && vCoins.size() > OUTPUT_GROUP_MAX_ENTRIES) { - // Cases where we have 11+ outputs all pointing to the same destination may result in - // privacy leaks as they will potentially be deterministically sorted. We solve that by - // explicitly shuffling the outputs before processing - Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext()); - } - - // Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the - // transaction at a target feerate. If an attempt fails, more attempts may be made using a more - // permissive CoinEligibilityFilter. - const bool res = [&] { - // Pre-selected inputs already cover the target amount. - if (value_to_select <= 0) return true; - - // If possible, fund the transaction with confirmed UTXOs only. Prefer at least six - // confirmations on outputs received from other wallets and only spend confirmed change. - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true; - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true; - - // Fall back to using zero confirmation change (but with as few ancestors in the mempool as - // possible) if we cannot fund the transaction otherwise. - if (m_spend_zero_conf_change) { - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) return true; - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), - vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) { - return true; - } - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), - vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) { - return true; - } - // If partial groups are allowed, relax the requirement of spending OutputGroups (groups - // of UTXOs sent to the same address, which are obviously controlled by a single wallet) - // in their entirety. - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), - vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) { - return true; - } - // Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs - // received from other wallets. - if (coin_control.m_include_unsafe_inputs - && SelectCoinsMinConf(value_to_select, - CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), - vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) { - return true; - } - // Try with unlimited ancestors/descendants. The transaction will still need to meet - // mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but - // OutputGroups use heuristics that may overestimate ancestor/descendant counts. - if (!fRejectLongChains && SelectCoinsMinConf(value_to_select, - CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */), - vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) { - return true; - } - } - // Coin Selection failed. - return false; - }(); - - // SelectCoinsMinConf clears setCoinsRet, so add the preset inputs from coin_control to the coinset - util::insert(setCoinsRet, setPresetCoins); - - // add preset inputs to the total value selected - nValueRet += nValueFromPresetInputs; - - return res; -} - bool CWallet::SignTransaction(CMutableTransaction& tx) const { AssertLockHeld(cs_wallet); @@ -2662,118 +1888,6 @@ SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkh return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl) -{ - std::vector<CRecipient> vecSend; - - // Turn the txout set into a CRecipient vector. - for (size_t idx = 0; idx < tx.vout.size(); idx++) { - const CTxOut& txOut = tx.vout[idx]; - CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; - vecSend.push_back(recipient); - } - - coinControl.fAllowOtherInputs = true; - - for (const CTxIn& txin : tx.vin) { - coinControl.Select(txin.prevout); - } - - // Acquire the locks to prevent races to the new locked unspents between the - // CreateTransaction call and LockCoin calls (when lockUnspents is true). - LOCK(cs_wallet); - - CTransactionRef tx_new; - FeeCalculation fee_calc_out; - if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) { - return false; - } - - if (nChangePosInOut != -1) { - tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]); - } - - // Copy output sizes from new transaction; they may have had the fee - // subtracted from them. - for (unsigned int idx = 0; idx < tx.vout.size(); idx++) { - tx.vout[idx].nValue = tx_new->vout[idx].nValue; - } - - // Add new txins while keeping original txin scriptSig/order. - for (const CTxIn& txin : tx_new->vin) { - if (!coinControl.IsSelected(txin.prevout)) { - tx.vin.push_back(txin); - - } - if (lockUnspents) { - LockCoin(txin.prevout); - } - - } - - return true; -} - -static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash) -{ - if (chain.isInitialBlockDownload()) { - return false; - } - constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds - int64_t block_time; - CHECK_NONFATAL(chain.findBlock(block_hash, FoundBlock().time(block_time))); - if (block_time < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) { - return false; - } - return true; -} - -/** - * Return a height-based locktime for new transactions (uses the height of the - * current chain tip unless we are not synced with the current chain - */ -static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height) -{ - uint32_t locktime; - // Discourage fee sniping. - // - // For a large miner the value of the transactions in the best block and - // the mempool can exceed the cost of deliberately attempting to mine two - // blocks to orphan the current best block. By setting nLockTime such that - // only the next block can include the transaction, we discourage this - // practice as the height restricted and limited blocksize gives miners - // considering fee sniping fewer options for pulling off this attack. - // - // A simple way to think about this is from the wallet's point of view we - // always want the blockchain to move forward. By setting nLockTime this - // way we're basically making the statement that we only want this - // transaction to appear in the next block; we don't want to potentially - // encourage reorgs by allowing transactions to appear at lower heights - // than the next block in forks of the best chain. - // - // Of course, the subsidy is high enough, and transaction volume low - // enough, that fee sniping isn't a problem yet, but by implementing a fix - // now we ensure code won't be written that makes assumptions about - // nLockTime that preclude a fix later. - if (IsCurrentForAntiFeeSniping(chain, block_hash)) { - locktime = block_height; - - // Secondly occasionally randomly pick a nLockTime even further back, so - // that transactions that are delayed after signing for whatever reason, - // e.g. high-latency mix networks and some CoinJoin implementations, have - // better privacy. - if (GetRandInt(10) == 0) - locktime = std::max(0, (int)locktime - GetRandInt(100)); - } else { - // If our chain is lagging behind, we can't discourage fee sniping nor help - // the privacy of high-latency transactions. To avoid leaking a potentially - // unique "nLockTime fingerprint", set nLockTime to a constant. - locktime = 0; - } - assert(locktime < LOCKTIME_THRESHOLD); - return locktime; -} - OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const { // If -changetype is specified, always use that change type. @@ -2802,413 +1916,6 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang return m_default_address_type; } -bool CWallet::CreateTransactionInternal( - const std::vector<CRecipient>& vecSend, - CTransactionRef& tx, - CAmount& nFeeRet, - int& nChangePosInOut, - bilingual_str& error, - const CCoinControl& coin_control, - FeeCalculation& fee_calc_out, - bool sign) -{ - CAmount nValue = 0; - const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); - ReserveDestination reservedest(this, change_type); - int nChangePosRequest = nChangePosInOut; - unsigned int nSubtractFeeFromAmount = 0; - for (const auto& recipient : vecSend) - { - if (nValue < 0 || recipient.nAmount < 0) - { - error = _("Transaction amounts must not be negative"); - return false; - } - nValue += recipient.nAmount; - - if (recipient.fSubtractFeeFromAmount) - nSubtractFeeFromAmount++; - } - if (vecSend.empty()) - { - error = _("Transaction must have at least one recipient"); - return false; - } - - CMutableTransaction txNew; - FeeCalculation feeCalc; - CAmount nFeeNeeded; - std::pair<int64_t, int64_t> tx_sizes; - int nBytes; - { - std::set<CInputCoin> setCoins; - LOCK(cs_wallet); - txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight()); - { - std::vector<COutput> vAvailableCoins; - AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0); - CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy - coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; - - // Create change script that will be used if we need change - // TODO: pass in scriptChange instead of reservedest so - // change transaction isn't always pay-to-bitcoin-address - CScript scriptChange; - - // coin control: send change to custom address - if (!std::get_if<CNoDestination>(&coin_control.destChange)) { - scriptChange = GetScriptForDestination(coin_control.destChange); - } else { // no coin control: send change to newly generated address - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool. If it fails, provide a dummy - // destination in case we don't need change. - CTxDestination dest; - if (!reservedest.GetReservedDestination(dest, true)) { - error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first."); - } - scriptChange = GetScriptForDestination(dest); - // A valid destination implies a change script (and - // vice-versa). An empty change script will abort later, if the - // change keypool ran out, but change is required. - CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty()); - } - CTxOut change_prototype_txout(0, scriptChange); - coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); - - // Set discard feerate - coin_selection_params.m_discard_feerate = GetDiscardRate(*this); - - // Get the fee rate to use effective values in coin selection - coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc); - // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly - // provided one - if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) { - error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB)); - return false; - } - if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) { - // eventually allow a fallback fee - error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); - return false; - } - - // Get long term estimate - CCoinControl cc_temp; - cc_temp.m_confirm_target = chain().estimateMaxBlocks(); - coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr); - - nFeeRet = 0; - bool pick_new_inputs = true; - CAmount nValueIn = 0; - - // BnB selector is the only selector used when this is true. - // That should only happen on the first pass through the loop. - coin_selection_params.use_bnb = true; - coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values - // Start with no fee and loop until there is enough fee - while (true) - { - nChangePosInOut = nChangePosRequest; - txNew.vin.clear(); - txNew.vout.clear(); - bool fFirst = true; - - CAmount nValueToSelect = nValue; - if (nSubtractFeeFromAmount == 0) - nValueToSelect += nFeeRet; - - // vouts to the payees - if (!coin_selection_params.m_subtract_fee_outputs) { - coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size) - } - for (const auto& recipient : vecSend) - { - CTxOut txout(recipient.nAmount, recipient.scriptPubKey); - - if (recipient.fSubtractFeeFromAmount) - { - assert(nSubtractFeeFromAmount != 0); - txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient - - if (fFirst) // first receiver pays the remainder not divisible by output count - { - fFirst = false; - txout.nValue -= nFeeRet % nSubtractFeeFromAmount; - } - } - // Include the fee cost for outputs. Note this is only used for BnB right now - if (!coin_selection_params.m_subtract_fee_outputs) { - coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION); - } - - if (IsDust(txout, chain().relayDustFee())) - { - if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) - { - if (txout.nValue < 0) - error = _("The transaction amount is too small to pay the fee"); - else - error = _("The transaction amount is too small to send after the fee has been deducted"); - } - else - error = _("Transaction amount too small"); - return false; - } - txNew.vout.push_back(txout); - } - - // Choose coins to use - bool bnb_used = false; - if (pick_new_inputs) { - nValueIn = 0; - setCoins.clear(); - int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this); - // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh - // as lower-bound to allow BnB to do it's thing - if (change_spend_size == -1) { - coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE; - } else { - coin_selection_params.change_spend_size = (size_t)change_spend_size; - } - if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used)) - { - // If BnB was used, it was the first pass. No longer the first pass and continue loop with knapsack. - if (bnb_used) { - coin_selection_params.use_bnb = false; - continue; - } - else { - error = _("Insufficient funds"); - return false; - } - } - } else { - bnb_used = false; - } - - const CAmount nChange = nValueIn - nValueToSelect; - if (nChange > 0) - { - // Fill a vout to ourself - CTxOut newTxOut(nChange, scriptChange); - - // Never create dust outputs; if we would, just - // add the dust to the fee. - // The nChange when BnB is used is always going to go to fees. - if (IsDust(newTxOut, coin_selection_params.m_discard_feerate) || bnb_used) - { - nChangePosInOut = -1; - nFeeRet += nChange; - } - else - { - if (nChangePosInOut == -1) - { - // Insert change txn at random position: - nChangePosInOut = GetRandInt(txNew.vout.size()+1); - } - else if ((unsigned int)nChangePosInOut > txNew.vout.size()) - { - error = _("Change index out of range"); - return false; - } - - std::vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut; - txNew.vout.insert(position, newTxOut); - } - } else { - nChangePosInOut = -1; - } - - // Dummy fill vin for maximum size estimation - // - for (const auto& coin : setCoins) { - txNew.vin.push_back(CTxIn(coin.outpoint,CScript())); - } - - tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); - nBytes = tx_sizes.first; - if (nBytes < 0) { - error = _("Signing transaction failed"); - return false; - } - - nFeeNeeded = coin_selection_params.m_effective_feerate.GetFee(nBytes); - if (nFeeRet >= nFeeNeeded) { - // Reduce fee to only the needed amount if possible. This - // prevents potential overpayment in fees if the coins - // selected to meet nFeeNeeded result in a transaction that - // requires less fee than the prior iteration. - - // If we have no change and a big enough excess fee, then - // try to construct transaction again only without picking - // new inputs. We now know we only need the smaller fee - // (because of reduced tx size) and so we should add a - // change output. Only try this once. - if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { - unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size - CAmount fee_needed_with_change = coin_selection_params.m_effective_feerate.GetFee(tx_size_with_change); - CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, coin_selection_params.m_discard_feerate); - if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) { - pick_new_inputs = false; - nFeeRet = fee_needed_with_change; - continue; - } - } - - // If we have change output already, just increase it - if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { - CAmount extraFeePaid = nFeeRet - nFeeNeeded; - std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut; - change_position->nValue += extraFeePaid; - nFeeRet -= extraFeePaid; - } - break; // Done, enough fee included. - } - else if (!pick_new_inputs) { - // This shouldn't happen, we should have had enough excess - // fee to pay for the new output and still meet nFeeNeeded - // Or we should have just subtracted fee from recipients and - // nFeeNeeded should not have changed - error = _("Transaction fee and change calculation failed"); - return false; - } - - // Try to reduce change to include necessary fee - if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { - CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; - std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut; - // Only reduce change if remaining amount is still a large enough output. - if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { - change_position->nValue -= additionalFeeNeeded; - nFeeRet += additionalFeeNeeded; - break; // Done, able to increase fee from change - } - } - - // If subtracting fee from recipients, we now know what fee we - // need to subtract, we have no reason to reselect inputs - if (nSubtractFeeFromAmount > 0) { - pick_new_inputs = false; - } - - // Include more fee and try again. - nFeeRet = nFeeNeeded; - coin_selection_params.use_bnb = false; - continue; - } - - // Give up if change keypool ran out and change is required - if (scriptChange.empty() && nChangePosInOut != -1) { - return false; - } - } - - // Shuffle selected coins and fill in final vin - txNew.vin.clear(); - std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end()); - Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext()); - - // Note how the sequence number is set to non-maxint so that - // the nLockTime set above actually works. - // - // BIP125 defines opt-in RBF as any nSequence < maxint-1, so - // we use the highest possible value in that range (maxint-2) - // to avoid conflicting with other possible uses of nSequence, - // and in the spirit of "smallest possible change from prior - // behavior." - const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); - for (const auto& coin : selected_coins) { - txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); - } - - if (sign && !SignTransaction(txNew)) { - error = _("Signing transaction failed"); - return false; - } - - // Return the constructed transaction data. - tx = MakeTransactionRef(std::move(txNew)); - - // Limit size - if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) || - (!sign && tx_sizes.second > MAX_STANDARD_TX_WEIGHT)) - { - error = _("Transaction too large"); - return false; - } - } - - if (nFeeRet > m_default_max_tx_fee) { - error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED); - return false; - } - - if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { - // Lastly, ensure this tx will pass the mempool's chain limits - if (!chain().checkChainLimits(tx)) { - error = _("Transaction has too long of a mempool chain"); - return false; - } - } - - // Before we return success, we assume any change key will be used to prevent - // accidental re-use. - reservedest.KeepDestination(); - fee_calc_out = feeCalc; - - WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", - nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, - feeCalc.est.pass.start, feeCalc.est.pass.end, - (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0, - feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, - feeCalc.est.fail.start, feeCalc.est.fail.end, - (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0, - feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool); - return true; -} - -bool CWallet::CreateTransaction( - const std::vector<CRecipient>& vecSend, - CTransactionRef& tx, - CAmount& nFeeRet, - int& nChangePosInOut, - bilingual_str& error, - const CCoinControl& coin_control, - FeeCalculation& fee_calc_out, - bool sign) -{ - int nChangePosIn = nChangePosInOut; - Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr) - bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign); - // try with avoidpartialspends unless it's enabled already - if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) { - CCoinControl tmp_cc = coin_control; - tmp_cc.m_avoid_partial_spends = true; - CAmount nFeeRet2; - CTransactionRef tx2; - int nChangePosInOut2 = nChangePosIn; - bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results - if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) { - // if fee of this alternative one is within the range of the max fee, we use this one - const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee; - WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped"); - if (use_aps) { - tx = tx2; - nFeeRet = nFeeRet2; - nChangePosInOut = nChangePosInOut2; - } - } - } - return res; -} - void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm) { LOCK(cs_wallet); @@ -3452,137 +2159,6 @@ void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations } } -std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const -{ - std::map<CTxDestination, CAmount> balances; - - { - LOCK(cs_wallet); - std::set<uint256> trusted_parents; - for (const auto& walletEntry : mapWallet) - { - const CWalletTx& wtx = walletEntry.second; - - if (!IsTrusted(wtx, trusted_parents)) - continue; - - if (wtx.IsImmatureCoinBase()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1)) - continue; - - for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) - { - CTxDestination addr; - if (!IsMine(wtx.tx->vout[i])) - continue; - if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr)) - continue; - - CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue; - balances[addr] += n; - } - } - } - - return balances; -} - -std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const -{ - AssertLockHeld(cs_wallet); - std::set< std::set<CTxDestination> > groupings; - std::set<CTxDestination> grouping; - - for (const auto& walletEntry : mapWallet) - { - const CWalletTx& wtx = walletEntry.second; - - if (wtx.tx->vin.size() > 0) - { - bool any_mine = false; - // group all input addresses with each other - for (const CTxIn& txin : wtx.tx->vin) - { - CTxDestination address; - if(!IsMine(txin)) /* If this input isn't mine, ignore it */ - continue; - if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address)) - continue; - grouping.insert(address); - any_mine = true; - } - - // group change with input addresses - if (any_mine) - { - for (const CTxOut& txout : wtx.tx->vout) - if (IsChange(txout)) - { - CTxDestination txoutAddr; - if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) - continue; - grouping.insert(txoutAddr); - } - } - if (grouping.size() > 0) - { - groupings.insert(grouping); - grouping.clear(); - } - } - - // group lone addrs by themselves - for (const auto& txout : wtx.tx->vout) - if (IsMine(txout)) - { - CTxDestination address; - if(!ExtractDestination(txout.scriptPubKey, address)) - continue; - grouping.insert(address); - groupings.insert(grouping); - grouping.clear(); - } - } - - std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses - std::map< CTxDestination, std::set<CTxDestination>* > setmap; // map addresses to the unique group containing it - for (std::set<CTxDestination> _grouping : groupings) - { - // make a set of all the groups hit by this new group - std::set< std::set<CTxDestination>* > hits; - std::map< CTxDestination, std::set<CTxDestination>* >::iterator it; - for (const CTxDestination& address : _grouping) - if ((it = setmap.find(address)) != setmap.end()) - hits.insert((*it).second); - - // merge all hit groups into a new single group and delete old groups - std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping); - for (std::set<CTxDestination>* hit : hits) - { - merged->insert(hit->begin(), hit->end()); - uniqueGroupings.erase(hit); - delete hit; - } - uniqueGroupings.insert(merged); - - // update setmap - for (const CTxDestination& element : *merged) - setmap[element] = merged; - } - - std::set< std::set<CTxDestination> > ret; - for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings) - { - ret.insert(*uniqueGrouping); - delete uniqueGrouping; - } - - return ret; -} - std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) const { LOCK(cs_wallet); @@ -4298,92 +2874,6 @@ bool CWalletTx::IsImmatureCoinBase() const return GetBlocksToMaturity() > 0; } -std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool separate_coins, const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate, const CoinEligibilityFilter& filter, bool positive_only) const -{ - std::vector<OutputGroup> groups_out; - - if (separate_coins) { - // Single coin means no grouping. Each COutput gets its own OutputGroup. - for (const COutput& output : outputs) { - // Skip outputs we cannot spend - if (!output.fSpendable) continue; - - size_t ancestors, descendants; - chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); - CInputCoin input_coin = output.GetInputCoin(); - - // Make an OutputGroup containing just this output - OutputGroup group{effective_feerate, long_term_feerate}; - group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); - - // Check the OutputGroup's eligibility. Only add the eligible ones. - if (positive_only && group.effective_value <= 0) continue; - if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); - } - return groups_out; - } - - // We want to combine COutputs that have the same scriptPubKey into single OutputGroups - // except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup. - // To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups. - // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added - // to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has - // OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector. - std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map; - for (const auto& output : outputs) { - // Skip outputs we cannot spend - if (!output.fSpendable) continue; - - size_t ancestors, descendants; - chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); - CInputCoin input_coin = output.GetInputCoin(); - CScript spk = input_coin.txout.scriptPubKey; - - std::vector<OutputGroup>& groups = spk_to_groups_map[spk]; - - if (groups.size() == 0) { - // No OutputGroups for this scriptPubKey yet, add one - groups.emplace_back(effective_feerate, long_term_feerate); - } - - // Get the last OutputGroup in the vector so that we can add the CInputCoin to it - // A pointer is used here so that group can be reassigned later if it is full. - OutputGroup* group = &groups.back(); - - // Check if this OutputGroup is full. We limit to OUTPUT_GROUP_MAX_ENTRIES when using -avoidpartialspends - // to avoid surprising users with very high fees. - if (group->m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) { - // The last output group is full, add a new group to the vector and use that group for the insertion - groups.emplace_back(effective_feerate, long_term_feerate); - group = &groups.back(); - } - - // Add the input_coin to group - group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); - } - - // Now we go through the entire map and pull out the OutputGroups - for (const auto& spk_and_groups_pair: spk_to_groups_map) { - const std::vector<OutputGroup>& groups_per_spk= spk_and_groups_pair.second; - - // Go through the vector backwards. This allows for the first item we deal with being the partial group. - for (auto group_it = groups_per_spk.rbegin(); group_it != groups_per_spk.rend(); group_it++) { - const OutputGroup& group = *group_it; - - // Don't include partial groups if there are full groups too and we don't want partial groups - if (group_it == groups_per_spk.rbegin() && groups_per_spk.size() > 1 && !filter.m_include_partial_groups) { - continue; - } - - // Check the OutputGroup's eligibility. Only add the eligible ones. - if (positive_only && group.effective_value <= 0) continue; - if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); - } - } - - return groups_out; -} - bool CWallet::IsCrypted() const { return HasEncryptionKeys(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index fc4edd8d20..4a3e0f7054 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -21,8 +21,11 @@ #include <validationinterface.h> #include <wallet/coinselection.h> #include <wallet/crypter.h> -#include <wallet/scriptpubkeyman.h> #include <external_signer.h> +#include <wallet/receive.h> +#include <wallet/scriptpubkeyman.h> +#include <wallet/spend.h> +#include <wallet/transaction.h> #include <wallet/walletdb.h> #include <wallet/walletutil.h> @@ -215,443 +218,6 @@ struct CRecipient bool fSubtractFeeFromAmount; }; -typedef std::map<std::string, std::string> mapValue_t; - - -static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) -{ - if (!mapValue.count("n")) - { - nOrderPos = -1; // TODO: calculate elsewhere - return; - } - nOrderPos = atoi64(mapValue["n"]); -} - - -static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) -{ - if (nOrderPos == -1) - return; - mapValue["n"] = ToString(nOrderPos); -} - -struct COutputEntry -{ - CTxDestination destination; - CAmount amount; - int vout; -}; - -/** Legacy class used for deserializing vtxPrev for backwards compatibility. - * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3, - * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs. - * These need to get deserialized for field alignment when deserializing - * a CWalletTx, but the deserialized values are discarded.**/ -class CMerkleTx -{ -public: - template<typename Stream> - void Unserialize(Stream& s) - { - CTransactionRef tx; - uint256 hashBlock; - std::vector<uint256> vMerkleBranch; - int nIndex; - - s >> tx >> hashBlock >> vMerkleBranch >> nIndex; - } -}; - -//Get the marginal bytes of spending the specified output -int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false); - -/** - * A transaction with a bunch of additional info that only the owner cares about. - * It includes any unrecorded transactions needed to link it back to the block chain. - */ -class CWalletTx -{ -private: - const CWallet* const pwallet; - - /** Constant used in hashBlock to indicate tx has been abandoned, only used at - * serialization/deserialization to avoid ambiguity with conflicted. - */ - static constexpr const uint256& ABANDON_HASH = uint256::ONE; - -public: - /** - * Key/value map with information about the transaction. - * - * The following keys can be read and written through the map and are - * serialized in the wallet database: - * - * "comment", "to" - comment strings provided to sendtoaddress, - * and sendmany wallet RPCs - * "replaces_txid" - txid (as HexStr) of transaction replaced by - * bumpfee on transaction created by bumpfee - * "replaced_by_txid" - txid (as HexStr) of transaction created by - * bumpfee on transaction replaced by bumpfee - * "from", "message" - obsolete fields that could be set in UI prior to - * 2011 (removed in commit 4d9b223) - * - * The following keys are serialized in the wallet database, but shouldn't - * be read or written through the map (they will be temporarily added and - * removed from the map during serialization): - * - * "fromaccount" - serialized strFromAccount value - * "n" - serialized nOrderPos value - * "timesmart" - serialized nTimeSmart value - * "spent" - serialized vfSpent value that existed prior to - * 2014 (removed in commit 93a18a3) - */ - mapValue_t mapValue; - std::vector<std::pair<std::string, std::string> > vOrderForm; - unsigned int fTimeReceivedIsTxTime; - unsigned int nTimeReceived; //!< time received by this node - /** - * Stable timestamp that never changes, and reflects the order a transaction - * was added to the wallet. Timestamp is based on the block time for a - * transaction added as part of a block, or else the time when the - * transaction was received if it wasn't part of a block, with the timestamp - * adjusted in both cases so timestamp order matches the order transactions - * were added to the wallet. More details can be found in - * CWallet::ComputeTimeSmart(). - */ - unsigned int nTimeSmart; - /** - * From me flag is set to 1 for transactions that were created by the wallet - * on this bitcoin node, and set to 0 for transactions that were created - * externally and came in through the network or sendrawtransaction RPC. - */ - bool fFromMe; - int64_t nOrderPos; //!< position in ordered transaction list - std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered; - - // memory only - enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS }; - CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const; - mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS]; - /** - * This flag is true if all m_amounts caches are empty. This is particularly - * useful in places where MarkDirty is conditionally called and the - * condition can be expensive and thus can be skipped if the flag is true. - * See MarkDestinationsDirty. - */ - mutable bool m_is_cache_empty{true}; - mutable bool fChangeCached; - mutable bool fInMempool; - mutable CAmount nChangeCached; - - CWalletTx(const CWallet* wallet, CTransactionRef arg) - : pwallet(wallet), - tx(std::move(arg)) - { - Init(); - } - - void Init() - { - mapValue.clear(); - vOrderForm.clear(); - fTimeReceivedIsTxTime = false; - nTimeReceived = 0; - nTimeSmart = 0; - fFromMe = false; - fChangeCached = false; - fInMempool = false; - nChangeCached = 0; - nOrderPos = -1; - m_confirm = Confirmation{}; - } - - CTransactionRef tx; - - /** New transactions start as UNCONFIRMED. At BlockConnected, - * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected, - * they roll back to UNCONFIRMED. If we detect a conflicting transaction at - * block connection, we update conflicted tx and its dependencies as CONFLICTED. - * If tx isn't confirmed and outside of mempool, the user may switch it to ABANDONED - * by using the abandontransaction call. This last status may be override by a CONFLICTED - * or CONFIRMED transition. - */ - enum Status { - UNCONFIRMED, - CONFIRMED, - CONFLICTED, - ABANDONED - }; - - /** Confirmation includes tx status and a triplet of {block height/block hash/tx index in block} - * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned. - * Meaning of these fields changes with CONFLICTED state where they instead point to block hash - * and block height of the deepest conflicting tx. - */ - struct Confirmation { - Status status; - int block_height; - uint256 hashBlock; - int nIndex; - Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {} - }; - - Confirmation m_confirm; - - template<typename Stream> - void Serialize(Stream& s) const - { - mapValue_t mapValueCopy = mapValue; - - mapValueCopy["fromaccount"] = ""; - WriteOrderPos(nOrderPos, mapValueCopy); - if (nTimeSmart) { - mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart); - } - - std::vector<char> dummy_vector1; //!< Used to be vMerkleBranch - std::vector<char> dummy_vector2; //!< Used to be vtxPrev - bool dummy_bool = false; //!< Used to be fSpent - uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock; - int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex; - s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool; - } - - template<typename Stream> - void Unserialize(Stream& s) - { - Init(); - - std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch - std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev - bool dummy_bool; //! Used to be fSpent - int serializedIndex; - s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool; - - /* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to - * the earliest block in the chain we know this or any in-wallet ancestor conflicts - * with. If nIndex == -1 and hashBlock is ABANDON_HASH, it means transaction is abandoned. - * In same context, an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) or - * unconfirmed one. Older clients interpret nIndex == -1 as unconfirmed for backward - * compatibility (pre-commit 9ac63d6). - */ - if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) { - setAbandoned(); - } else if (serializedIndex == -1) { - setConflicted(); - } else if (!m_confirm.hashBlock.IsNull()) { - m_confirm.nIndex = serializedIndex; - setConfirmed(); - } - - ReadOrderPos(nOrderPos, mapValue); - nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; - - mapValue.erase("fromaccount"); - mapValue.erase("spent"); - mapValue.erase("n"); - mapValue.erase("timesmart"); - } - - void SetTx(CTransactionRef arg) - { - tx = std::move(arg); - } - - //! make sure balances are recalculated - void MarkDirty() - { - m_amounts[DEBIT].Reset(); - m_amounts[CREDIT].Reset(); - m_amounts[IMMATURE_CREDIT].Reset(); - m_amounts[AVAILABLE_CREDIT].Reset(); - fChangeCached = false; - m_is_cache_empty = true; - } - - //! filter decides which addresses will count towards the debit - CAmount GetDebit(const isminefilter& filter) const; - CAmount GetCredit(const isminefilter& filter) const; - CAmount GetImmatureCredit(bool fUseCache = true) const; - // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct - // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The - // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid - // having to resolve the issue of member access into incomplete type CWallet. - CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS; - CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const; - CAmount GetChange() const; - - /** Get the marginal bytes if spending the specified output from this transaction */ - int GetSpendSize(unsigned int out, bool use_max_sig = false) const - { - return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig); - } - - void GetAmounts(std::list<COutputEntry>& listReceived, - std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const; - - bool IsFromMe(const isminefilter& filter) const - { - return (GetDebit(filter) > 0); - } - - /** True if only scriptSigs are different */ - bool IsEquivalentTo(const CWalletTx& tx) const; - - bool InMempool() const; - bool IsTrusted() const; - - int64_t GetTxTime() const; - - /** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */ - bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay); - - // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct - // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation - // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to - // resolve the issue of member access into incomplete type CWallet. Note - // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)" - // in place. - std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS; - - /** - * Return depth of transaction in blockchain: - * <0 : conflicts with a transaction this deep in the blockchain - * 0 : in memory pool, waiting to be included in a block - * >=1 : this many blocks deep in the main chain - */ - // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct - // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation - // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to - // resolve the issue of member access into incomplete type CWallet. Note - // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)" - // in place. - int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS; - bool IsInMainChain() const { return GetDepthInMainChain() > 0; } - - /** - * @return number of blocks to maturity for this transaction: - * 0 : is not a coinbase transaction, or is a mature coinbase transaction - * >0 : is a coinbase transaction which matures in this many blocks - */ - int GetBlocksToMaturity() const; - bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; } - void setAbandoned() - { - m_confirm.status = CWalletTx::ABANDONED; - m_confirm.hashBlock = uint256(); - m_confirm.block_height = 0; - m_confirm.nIndex = 0; - } - bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; } - void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; } - bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; } - void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; } - bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; } - void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; } - const uint256& GetHash() const { return tx->GetHash(); } - bool IsCoinBase() const { return tx->IsCoinBase(); } - bool IsImmatureCoinBase() const; - - // Disable copying of CWalletTx objects to prevent bugs where instances get - // copied in and out of the mapWallet map, and fields are updated in the - // wrong copy. - CWalletTx(CWalletTx const &) = delete; - void operator=(CWalletTx const &x) = delete; -}; - -class COutput -{ -public: - const CWalletTx *tx; - - /** Index in tx->vout. */ - int i; - - /** - * Depth in block chain. - * If > 0: the tx is on chain and has this many confirmations. - * If = 0: the tx is waiting confirmation. - * If < 0: a conflicting tx is on chain and has this many confirmations. */ - int nDepth; - - /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */ - int nInputBytes; - - /** Whether we have the private keys to spend this output */ - bool fSpendable; - - /** Whether we know how to spend this output, ignoring the lack of keys */ - bool fSolvable; - - /** Whether to use the maximum sized, 72 byte signature when calculating the size of the input spend. This should only be set when watch-only outputs are allowed */ - bool use_max_sig; - - /** - * Whether this output is considered safe to spend. Unconfirmed transactions - * from outside keys and unconfirmed replacement transactions are considered - * unsafe and will not be used to fund new spending transactions. - */ - bool fSafe; - - COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false) - { - tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in; - // If known and signable by the given wallet, compute nInputBytes - // Failure will keep this value -1 - if (fSpendable && tx) { - nInputBytes = tx->GetSpendSize(i, use_max_sig); - } - } - - std::string ToString() const; - - inline CInputCoin GetInputCoin() const - { - return CInputCoin(tx->tx, i, nInputBytes); - } -}; - -/** Parameters for one iteration of Coin Selection. */ -struct CoinSelectionParams -{ - /** Toggles use of Branch and Bound instead of Knapsack solver. */ - bool use_bnb = true; - /** Size of a change output in bytes, determined by the output type. */ - size_t change_output_size = 0; - /** Size of the input to spend a change output in virtual bytes. */ - size_t change_spend_size = 0; - /** The targeted feerate of the transaction being built. */ - CFeeRate m_effective_feerate; - /** The feerate estimate used to estimate an upper bound on what should be sufficient to spend - * the change output sometime in the future. */ - CFeeRate m_long_term_feerate; - /** If the cost to spend a change output at the discard feerate exceeds its value, drop it to fees. */ - CFeeRate m_discard_feerate; - /** Size of the transaction before coin selection, consisting of the header and recipient - * output(s), excluding the inputs and change output(s). */ - size_t tx_noinputs_size = 0; - /** Indicate that we are subtracting the fee from outputs */ - bool m_subtract_fee_outputs = false; - /** When true, always spend all (up to OUTPUT_GROUP_MAX_ENTRIES) or none of the outputs - * associated with the same address. This helps reduce privacy leaks resulting from address - * reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */ - bool m_avoid_partial_spends = false; - - CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_feerate, - CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size, bool avoid_partial) : - use_bnb(use_bnb), - change_output_size(change_output_size), - change_spend_size(change_spend_size), - m_effective_feerate(effective_feerate), - m_long_term_feerate(long_term_feerate), - m_discard_feerate(discard_feerate), - tx_noinputs_size(tx_noinputs_size), - m_avoid_partial_spends(avoid_partial) - {} - CoinSelectionParams() {} -}; - class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime /** * A CWallet maintains a set of transactions and balances, and provides the ability to create new transactions. @@ -661,7 +227,6 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati private: CKeyingMaterial vMasterKey GUARDED_BY(cs_wallet); - bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false); std::atomic<bool> fAbortRescan{false}; @@ -792,7 +357,7 @@ public: * from coin_control and Coin Selection if successful. */ bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, - const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** Get a name for this wallet for logging/debugging purposes. */ @@ -881,7 +446,7 @@ public: * param@[out] nValueRet Used to return the total value of selected coins. */ bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins, - std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const; + std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const; bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -889,7 +454,7 @@ public: bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool separate_coins, const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate, const CoinEligibilityFilter& filter, bool positive_only) const; + std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const; /** Display address on an external signer. Returns false if external signer support is not compiled */ bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -1386,12 +951,17 @@ public: } }; +struct TxSize { + int64_t vsize{-1}; + int64_t weight{-1}; +}; + /** Calculate the size of the transaction assuming all signatures are max size * Use DummySignatureCreator, which inserts 71 byte signatures everywhere. * NOTE: this requires that all inputs must be in mapWallet (eg the tx should * be IsAllFromMe). */ -std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); -std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false); +TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); +TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false); //! Add wallet name to persistent configuration so it will be loaded on startup. bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name); diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 6c51944d81..d25aaa070d 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -26,7 +26,10 @@ from test_framework.script import ( ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal -from test_framework.wallet import MiniWallet +from test_framework.wallet import ( + MiniWallet, + MiniWalletMode, +) CLTV_HEIGHT = 1351 @@ -97,7 +100,7 @@ class BIP65Test(BitcoinTestFramework): def run_test(self): peer = self.nodes[0].add_p2p_connection(P2PInterface()) - wallet = MiniWallet(self.nodes[0], raw_script=True) + wallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_OP_TRUE) self.test_cltv_info(is_active=False) diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index d815ad83b1..5081867319 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -55,7 +55,10 @@ from test_framework.util import ( assert_equal, softfork_active, ) -from test_framework.wallet import MiniWallet +from test_framework.wallet import ( + MiniWallet, + MiniWalletMode, +) TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above) COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs @@ -181,7 +184,7 @@ class BIP68_112_113Test(BitcoinTestFramework): def run_test(self): self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore()) - self.miniwallet = MiniWallet(self.nodes[0], use_p2pk=True) + self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK) self.log.info("Generate blocks in the past for coinbase outputs.") long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 3b430139b1..eb027c554a 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -7,7 +7,10 @@ Test that the DERSIG soft-fork activates at (regtest) height 1251. """ -from test_framework.blocktools import create_coinbase, create_block, create_transaction +from test_framework.blocktools import ( + create_block, + create_coinbase, +) from test_framework.messages import msg_block from test_framework.p2p import P2PInterface from test_framework.script import CScript @@ -15,6 +18,10 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, ) +from test_framework.wallet import ( + MiniWallet, + MiniWalletMode, +) DERSIG_HEIGHT = 1251 @@ -46,8 +53,9 @@ class BIP66Test(BitcoinTestFramework): self.setup_clean_chain = True self.rpc_timeout = 240 - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() + def create_tx(self, input_txid): + utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid, mark_as_spent=False) + return self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['tx'] def test_dersig_info(self, *, is_active): assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'], @@ -60,17 +68,16 @@ class BIP66Test(BitcoinTestFramework): def run_test(self): peer = self.nodes[0].add_p2p_connection(P2PInterface()) + self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK) self.test_dersig_info(is_active=False) self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2) - self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2)] - self.nodeaddress = self.nodes[0].getnewaddress() + self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.miniwallet.generate(DERSIG_HEIGHT - 2)] self.log.info("Test that a transaction with non-DER signature can still appear in a block") - spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], - self.nodeaddress, amount=1.0) + spendtx = self.create_tx(self.coinbase_txids[0]) unDERify(spendtx) spendtx.rehash() @@ -104,8 +111,7 @@ class BIP66Test(BitcoinTestFramework): self.log.info("Test that transactions with non-DER signatures cannot appear in a block") block.nVersion = 3 - spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1], - self.nodeaddress, amount=1.0) + spendtx = self.create_tx(self.coinbase_txids[1]) unDERify(spendtx) spendtx.rehash() @@ -133,7 +139,7 @@ class BIP66Test(BitcoinTestFramework): peer.sync_with_ping() self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted") - block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) + block.vtx[1] = self.create_tx(self.coinbase_txids[1]) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index c4002f524a..12aac3ab65 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -67,7 +67,8 @@ class MempoolAcceptanceTest(BitcoinTestFramework): self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar')) - assert_raises_rpc_error(-8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22'])) + assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22']*26)) + assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=[])) assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar'])) self.log.info('A transaction already in the blockchain') diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 2a58f8b3f7..9a2817a6ee 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -69,6 +69,7 @@ class NetTest(BitcoinTestFramework): self.test_getaddednodeinfo() self.test_service_flags() self.test_getnodeaddresses() + self.test_addpeeraddress() def test_connection_count(self): self.log.info("Test getconnectioncount") @@ -236,6 +237,30 @@ class NetTest(BitcoinTestFramework): assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1) assert_raises_rpc_error(-8, "Network not recognized: Foo", self.nodes[0].getnodeaddresses, 1, "Foo") + def test_addpeeraddress(self): + self.log.info("Test addpeeraddress") + node = self.nodes[1] + + self.log.debug("Test that addpeerinfo is a hidden RPC") + # It is hidden from general help, but its detailed help may be called directly. + assert "addpeerinfo" not in node.help() + assert "addpeerinfo" in node.help("addpeerinfo") + + self.log.debug("Test that adding an empty address fails") + assert_equal(node.addpeeraddress(address="", port=8333), {"success": False}) + assert_equal(node.getnodeaddresses(count=0), []) + + self.log.debug("Test that adding a valid address succeeds") + assert_equal(node.addpeeraddress(address="1.2.3.4", port=8333), {"success": True}) + addrs = node.getnodeaddresses(count=0) + assert_equal(len(addrs), 1) + assert_equal(addrs[0]["address"], "1.2.3.4") + assert_equal(addrs[0]["port"], 8333) + + self.log.debug("Test that adding the same address again when already present fails") + assert_equal(node.addpeeraddress(address="1.2.3.4", port=8333), {"success": False}) + assert_equal(len(node.getnodeaddresses(count=0)), 1) + if __name__ == '__main__': NetTest().main() diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py new file mode 100755 index 0000000000..3d8d81d6b8 --- /dev/null +++ b/test/functional/rpc_packages.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""RPCs that handle raw transaction packages.""" + +from decimal import Decimal +from io import BytesIO +import random + +from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE +from test_framework.test_framework import BitcoinTestFramework +from test_framework.messages import ( + BIP125_SEQUENCE_NUMBER, + COIN, + CTransaction, + CTxInWitness, +) +from test_framework.script import ( + CScript, + OP_TRUE, +) +from test_framework.util import ( + assert_equal, + hex_str_to_bytes, +) + +class RPCPackagesTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + + def assert_testres_equal(self, package_hex, testres_expected): + """Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only + be used to test packages where the order does not matter. The ordering of transactions in package_hex and + testres_expected must match. + """ + shuffled_indeces = list(range(len(package_hex))) + random.shuffle(shuffled_indeces) + shuffled_package = [package_hex[i] for i in shuffled_indeces] + shuffled_testres = [testres_expected[i] for i in shuffled_indeces] + assert_equal(shuffled_testres, self.nodes[0].testmempoolaccept(shuffled_package)) + + def run_test(self): + self.log.info("Generate blocks to create UTXOs") + node = self.nodes[0] + self.privkeys = [node.get_deterministic_priv_key().key] + self.address = node.get_deterministic_priv_key().address + self.coins = [] + # The last 100 coinbase transactions are premature + for b in node.generatetoaddress(200, self.address)[:100]: + coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0] + self.coins.append({ + "txid": coinbase["txid"], + "amount": coinbase["vout"][0]["value"], + "scriptPubKey": coinbase["vout"][0]["scriptPubKey"], + }) + + # Create some transactions that can be reused throughout the test. Never submit these to mempool. + self.independent_txns_hex = [] + self.independent_txns_testres = [] + for _ in range(3): + coin = self.coins.pop() + rawtx = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}], + {self.address : coin["amount"] - Decimal("0.0001")}) + signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys) + assert signedtx["complete"] + testres = node.testmempoolaccept([signedtx["hex"]]) + assert testres[0]["allowed"] + self.independent_txns_hex.append(signedtx["hex"]) + # testmempoolaccept returns a list of length one, avoid creating a 2D list + self.independent_txns_testres.append(testres[0]) + self.independent_txns_testres_blank = [{ + "txid": res["txid"], "wtxid": res["wtxid"]} for res in self.independent_txns_testres] + + self.test_independent() + self.test_chain() + self.test_multiple_children() + self.test_multiple_parents() + self.test_conflicting() + self.test_rbf() + + def chain_transaction(self, parent_txid, parent_value, n=0, parent_locking_script=None): + """Build a transaction that spends parent_txid.vout[n] and produces one output with + amount = parent_value with a fee deducted. + Return tuple (CTransaction object, raw hex, nValue, scriptPubKey of the output created). + """ + node = self.nodes[0] + inputs = [{"txid": parent_txid, "vout": n}] + my_value = parent_value - Decimal("0.0001") + outputs = {self.address : my_value} + rawtx = node.createrawtransaction(inputs, outputs) + prevtxs = [{ + "txid": parent_txid, + "vout": n, + "scriptPubKey": parent_locking_script, + "amount": parent_value, + }] if parent_locking_script else None + signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys, prevtxs=prevtxs) + tx = CTransaction() + assert signedtx["complete"] + tx.deserialize(BytesIO(hex_str_to_bytes(signedtx["hex"]))) + return (tx, signedtx["hex"], my_value, tx.vout[0].scriptPubKey.hex()) + + def test_independent(self): + self.log.info("Test multiple independent transactions in a package") + node = self.nodes[0] + # For independent transactions, order doesn't matter. + self.assert_testres_equal(self.independent_txns_hex, self.independent_txns_testres) + + self.log.info("Test an otherwise valid package with an extra garbage tx appended") + garbage_tx = node.createrawtransaction([{"txid": "00" * 32, "vout": 5}], {self.address: 1}) + tx = CTransaction() + tx.deserialize(BytesIO(hex_str_to_bytes(garbage_tx))) + # Only the txid and wtxids are returned because validation is incomplete for the independent txns. + # Package validation is atomic: if the node cannot find a UTXO for any single tx in the package, + # it terminates immediately to avoid unnecessary, expensive signature verification. + package_bad = self.independent_txns_hex + [garbage_tx] + testres_bad = self.independent_txns_testres_blank + [{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "allowed": False, "reject-reason": "missing-inputs"}] + self.assert_testres_equal(package_bad, testres_bad) + + self.log.info("Check testmempoolaccept tells us when some transactions completed validation successfully") + coin = self.coins.pop() + tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}], + {self.address : coin["amount"] - Decimal("0.0001")}) + tx_bad_sig = CTransaction() + tx_bad_sig.deserialize(BytesIO(hex_str_to_bytes(tx_bad_sig_hex))) + testres_bad_sig = node.testmempoolaccept(self.independent_txns_hex + [tx_bad_sig_hex]) + # By the time the signature for the last transaction is checked, all the other transactions + # have been fully validated, which is why the node returns full validation results for all + # transactions here but empty results in other cases. + assert_equal(testres_bad_sig, self.independent_txns_testres + [{ + "txid": tx_bad_sig.rehash(), + "wtxid": tx_bad_sig.getwtxid(), "allowed": False, + "reject-reason": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)" + }]) + + self.log.info("Check testmempoolaccept reports txns in packages that exceed max feerate") + coin = self.coins.pop() + tx_high_fee_raw = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}], + {self.address : coin["amount"] - Decimal("0.999")}) + tx_high_fee_signed = node.signrawtransactionwithkey(hexstring=tx_high_fee_raw, privkeys=self.privkeys) + assert tx_high_fee_signed["complete"] + tx_high_fee = CTransaction() + tx_high_fee.deserialize(BytesIO(hex_str_to_bytes(tx_high_fee_signed["hex"]))) + testres_high_fee = node.testmempoolaccept([tx_high_fee_signed["hex"]]) + assert_equal(testres_high_fee, [ + {"txid": tx_high_fee.rehash(), "wtxid": tx_high_fee.getwtxid(), "allowed": False, "reject-reason": "max-fee-exceeded"} + ]) + package_high_fee = [tx_high_fee_signed["hex"]] + self.independent_txns_hex + testres_package_high_fee = node.testmempoolaccept(package_high_fee) + assert_equal(testres_package_high_fee, testres_high_fee + self.independent_txns_testres_blank) + + def test_chain(self): + node = self.nodes[0] + first_coin = self.coins.pop() + + # Chain of 25 transactions + parent_locking_script = None + txid = first_coin["txid"] + chain_hex = [] + chain_txns = [] + value = first_coin["amount"] + + for _ in range(25): + (tx, txhex, value, parent_locking_script) = self.chain_transaction(txid, value, 0, parent_locking_script) + txid = tx.rehash() + chain_hex.append(txhex) + chain_txns.append(tx) + + self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency") + assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]), + [{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "package-error": "package-not-sorted"} for tx in chain_txns[::-1]]) + + self.log.info("Testmempoolaccept a chain of 25 transactions") + testres_multiple = node.testmempoolaccept(rawtxs=chain_hex) + + testres_single = [] + # Test accept and then submit each one individually, which should be identical to package test accept + for rawtx in chain_hex: + testres = node.testmempoolaccept([rawtx]) + testres_single.append(testres[0]) + # Submit the transaction now so its child should have no problem validating + node.sendrawtransaction(rawtx) + assert_equal(testres_single, testres_multiple) + + # Clean up by clearing the mempool + node.generate(1) + + def test_multiple_children(self): + node = self.nodes[0] + + self.log.info("Testmempoolaccept a package in which a transaction has two children within the package") + first_coin = self.coins.pop() + value = (first_coin["amount"] - Decimal("0.0002")) / 2 # Deduct reasonable fee and make 2 outputs + inputs = [{"txid": first_coin["txid"], "vout": 0}] + outputs = [{self.address : value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE : value}] + rawtx = node.createrawtransaction(inputs, outputs) + + parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys) + parent_tx = CTransaction() + assert parent_signed["complete"] + parent_tx.deserialize(BytesIO(hex_str_to_bytes(parent_signed["hex"]))) + parent_txid = parent_tx.rehash() + assert node.testmempoolaccept([parent_signed["hex"]])[0]["allowed"] + + parent_locking_script_a = parent_tx.vout[0].scriptPubKey.hex() + child_value = value - Decimal("0.0001") + + # Child A + (_, tx_child_a_hex, _, _) = self.chain_transaction(parent_txid, child_value, 0, parent_locking_script_a) + assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"] + + # Child B + rawtx_b = node.createrawtransaction([{"txid": parent_txid, "vout": 1}], {self.address : child_value}) + tx_child_b = CTransaction() + tx_child_b.deserialize(BytesIO(hex_str_to_bytes(rawtx_b))) + tx_child_b.wit.vtxinwit = [CTxInWitness()] + tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] + tx_child_b_hex = tx_child_b.serialize().hex() + assert not node.testmempoolaccept([tx_child_b_hex])[0]["allowed"] + + self.log.info("Testmempoolaccept with entire package, should work with children in either order") + testres_multiple_ab = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]) + testres_multiple_ba = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex]) + assert all([testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba]) + + testres_single = [] + # Test accept and then submit each one individually, which should be identical to package testaccept + for rawtx in [parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]: + testres = node.testmempoolaccept([rawtx]) + testres_single.append(testres[0]) + # Submit the transaction now so its child should have no problem validating + node.sendrawtransaction(rawtx) + assert_equal(testres_single, testres_multiple_ab) + + def create_child_with_parents(self, parents_tx, values, locking_scripts): + """Creates a transaction that spends the first output of each parent in parents_tx.""" + num_parents = len(parents_tx) + total_value = sum(values) + inputs = [{"txid": tx.rehash(), "vout": 0} for tx in parents_tx] + outputs = {self.address : total_value - num_parents * Decimal("0.0001")} + rawtx_child = self.nodes[0].createrawtransaction(inputs, outputs) + prevtxs = [] + for i in range(num_parents): + prevtxs.append({"txid": parents_tx[i].rehash(), "vout": 0, "scriptPubKey": locking_scripts[i], "amount": values[i]}) + signedtx_child = self.nodes[0].signrawtransactionwithkey(hexstring=rawtx_child, privkeys=self.privkeys, prevtxs=prevtxs) + assert signedtx_child["complete"] + return signedtx_child["hex"] + + def test_multiple_parents(self): + node = self.nodes[0] + + self.log.info("Testmempoolaccept a package in which a transaction has multiple parents within the package") + for num_parents in [2, 10, 24]: + # Test a package with num_parents parents and 1 child transaction. + package_hex = [] + parents_tx = [] + values = [] + parent_locking_scripts = [] + for _ in range(num_parents): + parent_coin = self.coins.pop() + value = parent_coin["amount"] + (tx, txhex, value, parent_locking_script) = self.chain_transaction(parent_coin["txid"], value) + package_hex.append(txhex) + parents_tx.append(tx) + values.append(value) + parent_locking_scripts.append(parent_locking_script) + child_hex = self.create_child_with_parents(parents_tx, values, parent_locking_scripts) + # Package accept should work with the parents in any order (as long as parents come before child) + for _ in range(10): + random.shuffle(package_hex) + testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_hex]) + assert all([testres["allowed"] for testres in testres_multiple]) + + testres_single = [] + # Test accept and then submit each one individually, which should be identical to package testaccept + for rawtx in package_hex + [child_hex]: + testres_single.append(node.testmempoolaccept([rawtx])[0]) + # Submit the transaction now so its child should have no problem validating + node.sendrawtransaction(rawtx) + assert_equal(testres_single, testres_multiple) + + def test_conflicting(self): + node = self.nodes[0] + prevtx = self.coins.pop() + inputs = [{"txid": prevtx["txid"], "vout": 0}] + output1 = {node.get_deterministic_priv_key().address: 50 - 0.00125} + output2 = {ADDRESS_BCRT1_P2WSH_OP_TRUE: 50 - 0.00125} + + # tx1 and tx2 share the same inputs + rawtx1 = node.createrawtransaction(inputs, output1) + rawtx2 = node.createrawtransaction(inputs, output2) + signedtx1 = node.signrawtransactionwithkey(hexstring=rawtx1, privkeys=self.privkeys) + signedtx2 = node.signrawtransactionwithkey(hexstring=rawtx2, privkeys=self.privkeys) + tx1 = CTransaction() + tx1.deserialize(BytesIO(hex_str_to_bytes(signedtx1["hex"]))) + tx2 = CTransaction() + tx2.deserialize(BytesIO(hex_str_to_bytes(signedtx2["hex"]))) + assert signedtx1["complete"] + assert signedtx2["complete"] + + # Ensure tx1 and tx2 are valid by themselves + assert node.testmempoolaccept([signedtx1["hex"]])[0]["allowed"] + assert node.testmempoolaccept([signedtx2["hex"]])[0]["allowed"] + + self.log.info("Test duplicate transactions in the same package") + testres = node.testmempoolaccept([signedtx1["hex"], signedtx1["hex"]]) + assert_equal(testres, [ + {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"}, + {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"} + ]) + + self.log.info("Test conflicting transactions in the same package") + testres = node.testmempoolaccept([signedtx1["hex"], signedtx2["hex"]]) + assert_equal(testres, [ + {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"}, + {"txid": tx2.rehash(), "wtxid": tx2.getwtxid(), "package-error": "conflict-in-package"} + ]) + + def test_rbf(self): + node = self.nodes[0] + coin = self.coins.pop() + inputs = [{"txid": coin["txid"], "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}] + fee = Decimal('0.00125000') + output = {node.get_deterministic_priv_key().address: 50 - fee} + raw_replaceable_tx = node.createrawtransaction(inputs, output) + signed_replaceable_tx = node.signrawtransactionwithkey(hexstring=raw_replaceable_tx, privkeys=self.privkeys) + testres_replaceable = node.testmempoolaccept([signed_replaceable_tx["hex"]]) + replaceable_tx = CTransaction() + replaceable_tx.deserialize(BytesIO(hex_str_to_bytes(signed_replaceable_tx["hex"]))) + assert_equal(testres_replaceable, [ + {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), + "allowed": True, "vsize": replaceable_tx.get_vsize(), "fees": { "base": fee }} + ]) + + # Replacement transaction is identical except has double the fee + replacement_tx = CTransaction() + replacement_tx.deserialize(BytesIO(hex_str_to_bytes(signed_replaceable_tx["hex"]))) + replacement_tx.vout[0].nValue -= int(fee * COIN) # Doubled fee + signed_replacement_tx = node.signrawtransactionwithkey(replacement_tx.serialize().hex(), self.privkeys) + replacement_tx.deserialize(BytesIO(hex_str_to_bytes(signed_replacement_tx["hex"]))) + + self.log.info("Test that transactions within a package cannot replace each other") + testres_rbf_conflicting = node.testmempoolaccept([signed_replaceable_tx["hex"], signed_replacement_tx["hex"]]) + assert_equal(testres_rbf_conflicting, [ + {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), "package-error": "conflict-in-package"}, + {"txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "package-error": "conflict-in-package"} + ]) + + self.log.info("Test that packages cannot conflict with mempool transactions, even if a valid BIP125 RBF") + node.sendrawtransaction(signed_replaceable_tx["hex"]) + testres_rbf_single = node.testmempoolaccept([signed_replacement_tx["hex"]]) + # This transaction is a valid BIP125 replace-by-fee + assert testres_rbf_single[0]["allowed"] + testres_rbf_package = self.independent_txns_testres_blank + [{ + "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "allowed": False, "reject-reason": "txn-mempool-conflict" + }] + self.assert_testres_equal(self.independent_txns_hex + [signed_replacement_tx["hex"]], testres_rbf_package) + +if __name__ == "__main__": + RPCPackagesTest().main() diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 18822fc610..05bda5d899 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -5,6 +5,7 @@ """A limited-functionality wallet, which may replace a real wallet in tests""" from decimal import Decimal +from enum import Enum from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE from test_framework.key import ECKey from test_framework.messages import ( @@ -30,22 +31,46 @@ from test_framework.util import ( ) +class MiniWalletMode(Enum): + """Determines the transaction type the MiniWallet is creating and spending. + + For most purposes, the default mode ADDRESS_OP_TRUE should be sufficient; + it simply uses a fixed bech32 P2WSH address whose coins are spent with a + witness stack of OP_TRUE, i.e. following an anyone-can-spend policy. + However, if the transactions need to be modified by the user (e.g. prepending + scriptSig for testing opcodes that are activated by a soft-fork), or the txs + should contain an actual signature, the raw modes RAW_OP_TRUE and RAW_P2PK + can be useful. Summary of modes: + + | output | | tx is | can modify | needs + mode | description | address | standard | scriptSig | signing + ----------------+-------------------+-----------+----------+------------+---------- + ADDRESS_OP_TRUE | anyone-can-spend | bech32 | yes | no | no + RAW_OP_TRUE | anyone-can-spend | - (raw) | no | yes | no + RAW_P2PK | pay-to-public-key | - (raw) | yes | yes | yes + """ + ADDRESS_OP_TRUE = 1 + RAW_OP_TRUE = 2 + RAW_P2PK = 3 + + class MiniWallet: - def __init__(self, test_node, *, raw_script=False, use_p2pk=False): + def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE): self._test_node = test_node self._utxos = [] self._priv_key = None self._address = None - if raw_script: + assert isinstance(mode, MiniWalletMode) + if mode == MiniWalletMode.RAW_OP_TRUE: self._scriptPubKey = bytes(CScript([OP_TRUE])) - elif use_p2pk: + elif mode == MiniWalletMode.RAW_P2PK: # use simple deterministic private key (k=1) self._priv_key = ECKey() self._priv_key.set((1).to_bytes(32, 'big'), True) pub_key = self._priv_key.get_pubkey() self._scriptPubKey = bytes(CScript([pub_key.get_bytes(), OP_CHECKSIG])) - else: + elif mode == MiniWalletMode.ADDRESS_OP_TRUE: self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey']) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 00527e78f1..49f269f8b4 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -211,6 +211,7 @@ BASE_SCRIPTS = [ 'mempool_package_onemore.py', 'rpc_createmultisig.py --legacy-wallet', 'rpc_createmultisig.py --descriptors', + 'rpc_packages.py', 'feature_versionbits_warning.py', 'rpc_preciousblock.py', 'wallet_importprunedfunds.py --legacy-wallet', diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py index 1d3736d9b1..c13d8de4b5 100755 --- a/test/functional/wallet_avoidreuse.py +++ b/test/functional/wallet_avoidreuse.py @@ -42,25 +42,25 @@ def count_unspent(node): r["reused"]["supported"] = supports_reused return r -def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None): +def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None, margin=0.001): '''Make assertions about a node's unspent output statistics''' stats = count_unspent(node) if total_count is not None: assert_equal(stats["total"]["count"], total_count) if total_sum is not None: - assert_approx(stats["total"]["sum"], total_sum, 0.001) + assert_approx(stats["total"]["sum"], total_sum, margin) if reused_supported is not None: assert_equal(stats["reused"]["supported"], reused_supported) if reused_count is not None: assert_equal(stats["reused"]["count"], reused_count) if reused_sum is not None: - assert_approx(stats["reused"]["sum"], reused_sum, 0.001) + assert_approx(stats["reused"]["sum"], reused_sum, margin) -def assert_balances(node, mine): +def assert_balances(node, mine, margin=0.001): '''Make assertions about a node's getbalances output''' got = node.getbalances()["mine"] for k,v in mine.items(): - assert_approx(got[k], v, 0.001) + assert_approx(got[k], v, margin) class AvoidReuseTest(BitcoinTestFramework): @@ -299,7 +299,7 @@ class AvoidReuseTest(BitcoinTestFramework): ret_addr = self.nodes[0].getnewaddress() # send multiple transactions, reusing one address - for _ in range(11): + for _ in range(101): self.nodes[0].sendtoaddress(new_addr, 1) self.nodes[0].generate(1) @@ -311,14 +311,14 @@ class AvoidReuseTest(BitcoinTestFramework): # getbalances and listunspent should show the remaining outputs # in the reused address as used/reused - assert_unspent(self.nodes[1], total_count=2, total_sum=6, reused_count=1, reused_sum=1) - assert_balances(self.nodes[1], mine={"used": 1, "trusted": 5}) + assert_unspent(self.nodes[1], total_count=2, total_sum=96, reused_count=1, reused_sum=1, margin=0.01) + assert_balances(self.nodes[1], mine={"used": 1, "trusted": 95}, margin=0.01) def test_full_destination_group_is_preferred(self): ''' - Test the case where [1] only has 11 outputs of 1 BTC in the same reused + Test the case where [1] only has 101 outputs of 1 BTC in the same reused address and tries to send a small payment of 0.5 BTC. The wallet - should use 10 outputs from the reused address as inputs and not a + should use 100 outputs from the reused address as inputs and not a single 1 BTC input, in order to join several outputs from the reused address. ''' @@ -330,8 +330,8 @@ class AvoidReuseTest(BitcoinTestFramework): new_addr = self.nodes[1].getnewaddress() ret_addr = self.nodes[0].getnewaddress() - # Send 11 outputs of 1 BTC to the same, reused address in the wallet - for _ in range(11): + # Send 101 outputs of 1 BTC to the same, reused address in the wallet + for _ in range(101): self.nodes[0].sendtoaddress(new_addr, 1) self.nodes[0].generate(1) @@ -342,14 +342,14 @@ class AvoidReuseTest(BitcoinTestFramework): txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=0.5) inputs = self.nodes[1].getrawtransaction(txid, 1)["vin"] - # The transaction should use 10 inputs exactly - assert_equal(len(inputs), 10) + # The transaction should use 100 inputs exactly + assert_equal(len(inputs), 100) def test_all_destination_groups_are_used(self): ''' - Test the case where [1] only has 22 outputs of 1 BTC in the same reused - address and tries to send a payment of 20.5 BTC. The wallet - should use all 22 outputs from the reused address as inputs. + Test the case where [1] only has 202 outputs of 1 BTC in the same reused + address and tries to send a payment of 200.5 BTC. The wallet + should use all 202 outputs from the reused address as inputs. ''' self.log.info("Test that all destination groups are used") @@ -359,20 +359,20 @@ class AvoidReuseTest(BitcoinTestFramework): new_addr = self.nodes[1].getnewaddress() ret_addr = self.nodes[0].getnewaddress() - # Send 22 outputs of 1 BTC to the same, reused address in the wallet - for _ in range(22): + # Send 202 outputs of 1 BTC to the same, reused address in the wallet + for _ in range(202): self.nodes[0].sendtoaddress(new_addr, 1) self.nodes[0].generate(1) self.sync_all() # Sending a transaction that needs to use the full groups - # of 10 inputs but also the incomplete group of 2 inputs. - txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=20.5) + # of 100 inputs but also the incomplete group of 2 inputs. + txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=200.5) inputs = self.nodes[1].getrawtransaction(txid, 1)["vin"] - # The transaction should use 22 inputs exactly - assert_equal(len(inputs), 22) + # The transaction should use 202 inputs exactly + assert_equal(len(inputs), 202) if __name__ == '__main__': diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 354e14f361..07d613f62d 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -25,6 +25,10 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "wallet/fees -> wallet/wallet -> wallet/fees" "wallet/wallet -> wallet/walletdb -> wallet/wallet" "node/coinstats -> validation -> node/coinstats" + # Temporary circular dependencies that allow wallet.h/wallet.cpp to be + # split up in a MOVEONLY commit. These are removed in #21206. + "wallet/receive -> wallet/wallet -> wallet/receive" + "wallet/spend -> wallet/wallet -> wallet/spend" ) EXIT_CODE=0 |