mirror of
				https://github.com/XRPLF/rippled.git
				synced 2025-11-04 11:15:56 +00:00 
			
		
		
		
	Compare commits
	
		
			462 Commits
		
	
	
		
			9546c52013
			...
			ximinez/le
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7925cc4052 | ||
| 
						 | 
					245bac1769 | ||
| 
						 | 
					cdd6ad4ba4 | ||
| 
						 | 
					b18dece145 | ||
| 
						 | 
					63a08560ca | ||
| 
						 | 
					89979022f2 | ||
| 
						 | 
					4920e65b14 | ||
| 
						 | 
					8ac8a47c99 | ||
| 
						 | 
					12c4b5a632 | ||
| 
						 | 
					25c5e3b17f | ||
| 
						 | 
					8eb233c2ea | ||
| 
						 | 
					50fc93f742 | ||
| 
						 | 
					b472cc3493 | ||
| 
						 | 
					ab45a8a737 | ||
| 
						 | 
					dfafb141cc | ||
| 
						 | 
					4e32d2ed98 | ||
| 
						 | 
					fa69918124 | ||
| 
						 | 
					27389cd7e8 | ||
| 
						 | 
					2b9f70b0bd | ||
| 
						 | 
					e8dddcbb7f | ||
| 
						 | 
					7a758a42f9 | ||
| 
						 | 
					298f6deac9 | ||
| 
						 | 
					71565a909e | ||
| 
						 | 
					52490730b2 | ||
| 
						 | 
					64e0ee4be9 | ||
| 
						 | 
					d0c60cdf59 | ||
| 
						 | 
					0febc60cbe | ||
| 
						 | 
					122d8f96b3 | ||
| 
						 | 
					ec60dcf90d | ||
| 
						 | 
					310852ba2d | ||
| 
						 | 
					f4404eafbd | ||
| 
						 | 
					fe5bd82ed8 | ||
| 
						 | 
					da14c6f018 | ||
| 
						 | 
					5ceefdfbd5 | ||
| 
						 | 
					b8b9e7a51e | ||
| 
						 | 
					269fb44cd7 | ||
| 
						 | 
					40ef91e7e0 | ||
| 
						 | 
					fe4269cf8b | ||
| 
						 | 
					a3b82023d6 | ||
| 
						 | 
					0950d41fce | ||
| 
						 | 
					45819477d3 | ||
| 
						 | 
					83e249eb8a | ||
| 
						 | 
					07fcbd5498 | ||
| 
						 | 
					88a770c71b | ||
| 
						 | 
					78ef800e30 | ||
| 
						 | 
					7f2f6b8791 | ||
| 
						 | 
					7c45f6a673 | ||
| 
						 | 
					66b547f6fc | ||
| 
						 | 
					6adb2eca76 | ||
| 
						 | 
					f60e298627 | ||
| 
						 | 
					9f5bc8f0da | ||
| 
						 | 
					e306a7c75f | ||
| 
						 | 
					59364be5f8 | ||
| 
						 | 
					aa696ed61a | ||
| 
						 | 
					044b9b8217 | ||
| 
						 | 
					f78c5f65bc | ||
| 
						 | 
					6ad7d1c076 | ||
| 
						 | 
					9814ec0309 | ||
| 
						 | 
					b3593fac1c | ||
| 
						 | 
					30e2650ff9 | ||
| 
						 | 
					4aa6baef26 | ||
| 
						 | 
					dd938c4e7b | ||
| 
						 | 
					92144efbdf | ||
| 
						 | 
					5da586bffc | ||
| 
						 | 
					70ae693821 | ||
| 
						 | 
					f6098fd80b | ||
| 
						 | 
					8757022312 | ||
| 
						 | 
					587cb2533c | ||
| 
						 | 
					92431e11c8 | ||
| 
						 | 
					dfc4933c3b | ||
| 
						 | 
					1bb306baf4 | ||
| 
						 | 
					98d1cf07ed | ||
| 
						 | 
					baabf142e4 | ||
| 
						 | 
					f9bd9f390a | ||
| 
						 | 
					d7196a8e56 | ||
| 
						 | 
					bda6bb46d1 | ||
| 
						 | 
					ec20974cae | ||
| 
						 | 
					1efc532b21 | ||
| 
						 | 
					2dd239c59f | ||
| 
						 | 
					fc2163708a | ||
| 
						 | 
					eeec90ee74 | ||
| 
						 | 
					8d982758cb | ||
| 
						 | 
					1b31dbc4c9 | ||
| 
						 | 
					f8b63228b6 | ||
| 
						 | 
					0b8cd2d7ca | ||
| 
						 | 
					a8de91c57d | ||
| 
						 | 
					97e2c10359 | ||
| 
						 | 
					96d0258f51 | ||
| 
						 | 
					fb8dafa6a8 | ||
| 
						 | 
					3cc8e5564a | ||
| 
						 | 
					e1939d0d20 | ||
| 
						 | 
					2509293943 | ||
| 
						 | 
					63edf035a6 | ||
| 
						 | 
					d353f4a2e6 | ||
| 
						 | 
					9f1ed7ed61 | ||
| 
						 | 
					d700f6876d | ||
| 
						 | 
					9c28578262 | ||
| 
						 | 
					134998d86f | ||
| 
						 | 
					307d5474b4 | ||
| 
						 | 
					1212902be6 | ||
| 
						 | 
					48d458afc3 | ||
| 
						 | 
					e6e9003821 | ||
| 
						 | 
					66dbc05a9f | ||
| 
						 | 
					7f4cdefc24 | ||
| 
						 | 
					8152bcb1f3 | ||
| 
						 | 
					53e82a1c11 | ||
| 
						 | 
					c179135e15 | ||
| 
						 | 
					5117ef831e | ||
| 
						 | 
					f35d0dc829 | ||
| 
						 | 
					311da8fdcf | ||
| 
						 | 
					7120cce996 | ||
| 
						 | 
					23045fcbef | ||
| 
						 | 
					d576e9d214 | ||
| 
						 | 
					5863f05343 | ||
| 
						 | 
					ce23d0aa54 | ||
| 
						 | 
					aac25e8c79 | ||
| 
						 | 
					1888784e8e | ||
| 
						 | 
					681c179174 | ||
| 
						 | 
					05739c59cf | ||
| 
						 | 
					b127b24dae | ||
| 
						 | 
					c2a0824da0 | ||
| 
						 | 
					e1b234cc51 | ||
| 
						 | 
					03d8cf2c12 | ||
| 
						 | 
					7a3f7aebcf | ||
| 
						 | 
					f78d27e9b3 | ||
| 
						 | 
					e38e911190 | ||
| 
						 | 
					44313d9a30 | ||
| 
						 | 
					a3365545df | ||
| 
						 | 
					1d85727675 | ||
| 
						 | 
					da3c2beae0 | ||
| 
						 | 
					6b7136906f | ||
| 
						 | 
					7612fd0ba6 | ||
| 
						 | 
					0bd6b1ae49 | ||
| 
						 | 
					66216a3d41 | ||
| 
						 | 
					4b90f97e52 | ||
| 
						 | 
					f1a2fe3d0a | ||
| 
						 | 
					d76f31cb7c | ||
| 
						 | 
					b5caa9c63d | ||
| 
						 | 
					3931eb39d5 | ||
| 
						 | 
					322308f7d1 | ||
| 
						 | 
					4c8f7968c4 | ||
| 
						 | 
					6e2965f01a | ||
| 
						 | 
					dba3af9318 | ||
| 
						 | 
					7304ee68d5 | ||
| 
						 | 
					759d114ab8 | ||
| 
						 | 
					3c93dbbc7c | ||
| 
						 | 
					97819b23ba | ||
| 
						 | 
					9bfa88cd86 | ||
| 
						 | 
					de5af71cb6 | ||
| 
						 | 
					67578b8f50 | ||
| 
						 | 
					e72a9d4bf2 | ||
| 
						 | 
					5b576b841c | ||
| 
						 | 
					267b537a13 | ||
| 
						 | 
					543f1e702f | ||
| 
						 | 
					f9dcce2d7e | ||
| 
						 | 
					928af9633f | ||
| 
						 | 
					9e84c37306 | ||
| 
						 | 
					2aa626c205 | ||
| 
						 | 
					36c2fa1e56 | ||
| 
						 | 
					886b0a487c | ||
| 
						 | 
					7849df5feb | ||
| 
						 | 
					55baf35d9c | ||
| 
						 | 
					90ca9ea71a | ||
| 
						 | 
					47eb1e8db2 | ||
| 
						 | 
					540b76fe42 | ||
| 
						 | 
					d4135a9115 | ||
| 
						 | 
					4c6a34d3c1 | ||
| 
						 | 
					be5390b8c7 | ||
| 
						 | 
					b5ddc812ea | ||
| 
						 | 
					b9a2eb3399 | ||
| 
						 | 
					2c0c4567f4 | ||
| 
						 | 
					d1c708ca01 | ||
| 
						 | 
					ae27376863 | ||
| 
						 | 
					e237f968c7 | ||
| 
						 | 
					b02b58e3b5 | ||
| 
						 | 
					d10ac3b169 | ||
| 
						 | 
					c37f863201 | ||
| 
						 | 
					1fbd3fcfa4 | ||
| 
						 | 
					70755ed1d3 | ||
| 
						 | 
					6926c511cb | ||
| 
						 | 
					e01dd6d5b0 | ||
| 
						 | 
					0bfd404111 | ||
| 
						 | 
					5c072579d7 | ||
| 
						 | 
					c118121337 | ||
| 
						 | 
					8a4c607af9 | ||
| 
						 | 
					f85ce9c742 | ||
| 
						 | 
					49e1f97476 | ||
| 
						 | 
					47aa95fcc4 | ||
| 
						 | 
					dc5b0918da | ||
| 
						 | 
					cb991523f8 | ||
| 
						 | 
					9683344940 | ||
| 
						 | 
					5ac905be01 | ||
| 
						 | 
					4d5afbea3a | ||
| 
						 | 
					9d08fe9a03 | ||
| 
						 | 
					84e97f0ef5 | ||
| 
						 | 
					4d24b5eac2 | ||
| 
						 | 
					935749fe1f | ||
| 
						 | 
					49eb1cc54e | ||
| 
						 | 
					dee972e9cd | ||
| 
						 | 
					bdeed43304 | ||
| 
						 | 
					7eff303160 | ||
| 
						 | 
					aa1341f42e | ||
| 
						 | 
					91031dbec4 | ||
| 
						 | 
					03da5da815 | ||
| 
						 | 
					fc860c1a1d | ||
| 
						 | 
					2545bc5936 | ||
| 
						 | 
					9a9588eaf6 | ||
| 
						 | 
					abee3dd55c | ||
| 
						 | 
					22d9ade2b0 | ||
| 
						 | 
					3ea0fa67cb | ||
| 
						 | 
					1b295c7c00 | ||
| 
						 | 
					dd52a50acc | ||
| 
						 | 
					80a2d8f789 | ||
| 
						 | 
					e5f99e8ee7 | ||
| 
						 | 
					b925ed44a0 | ||
| 
						 | 
					14ff03821c | ||
| 
						 | 
					5ff6bc496a | ||
| 
						 | 
					7aaad4a8ef | ||
| 
						 | 
					1897ac5ee7 | ||
| 
						 | 
					5894cd2103 | ||
| 
						 | 
					2f89182fd3 | ||
| 
						 | 
					4191ddd702 | ||
| 
						 | 
					37888b97ba | ||
| 
						 | 
					10f7e4a02a | ||
| 
						 | 
					e5941d5b23 | ||
| 
						 | 
					3f1a0f3a3d | ||
| 
						 | 
					fa2c595a59 | ||
| 
						 | 
					fc6fe9802e | ||
| 
						 | 
					b3c40ad408 | ||
| 
						 | 
					779e94bc03 | ||
| 
						 | 
					f653ca03d9 | ||
| 
						 | 
					d46e477092 | ||
| 
						 | 
					24f58a2e94 | ||
| 
						 | 
					e3358ad124 | ||
| 
						 | 
					5102147709 | ||
| 
						 | 
					0a54c09de1 | ||
| 
						 | 
					d594ca1401 | ||
| 
						 | 
					5335c04616 | ||
| 
						 | 
					6db14ccb13 | ||
| 
						 | 
					582f56487d | ||
| 
						 | 
					d47c039941 | ||
| 
						 | 
					bf4ae57da4 | ||
| 
						 | 
					83702e79bf | ||
| 
						 | 
					6379363bad | ||
| 
						 | 
					f0c96ccfe6 | ||
| 
						 | 
					4c726838d1 | ||
| 
						 | 
					47bcc67d56 | ||
| 
						 | 
					4b48f9612b | ||
| 
						 | 
					7bba34832b | ||
| 
						 | 
					76ae61feb5 | ||
| 
						 | 
					adb260fe17 | ||
| 
						 | 
					8444a944af | ||
| 
						 | 
					6d20e59212 | ||
| 
						 | 
					c49cf6cec6 | ||
| 
						 | 
					e24f034b17 | ||
| 
						 | 
					72733b57e4 | ||
| 
						 | 
					7a78449f6d | ||
| 
						 | 
					0d500343ed | ||
| 
						 | 
					f15e88c009 | ||
| 
						 | 
					0069c0c5fa | ||
| 
						 | 
					450d38293d | ||
| 
						 | 
					6d9001edac | ||
| 
						 | 
					3dc14e7430 | ||
| 
						 | 
					9a8c2523ae | ||
| 
						 | 
					115161f7d2 | ||
| 
						 | 
					77fcb9b9b3 | ||
| 
						 | 
					d2e8144c53 | ||
| 
						 | 
					9d45fd1873 | ||
| 
						 | 
					4c43a0bca4 | ||
| 
						 | 
					bb491431bd | ||
| 
						 | 
					f090434c2d | ||
| 
						 | 
					5f7b4a5e13 | ||
| 
						 | 
					11b8f9978e | ||
| 
						 | 
					58551da698 | ||
| 
						 | 
					5ade8756f6 | ||
| 
						 | 
					ac4c4905df | ||
| 
						 | 
					a5f1879afb | ||
| 
						 | 
					8a77ac71ed | ||
| 
						 | 
					a72c237373 | ||
| 
						 | 
					faae2514b9 | ||
| 
						 | 
					bcbbf71f29 | ||
| 
						 | 
					12994d4251 | ||
| 
						 | 
					22679673f6 | ||
| 
						 | 
					a14cbf117c | ||
| 
						 | 
					5ce07e769f | ||
| 
						 | 
					08a5e8428a | ||
| 
						 | 
					a8e35f8399 | ||
| 
						 | 
					1f85888ae8 | ||
| 
						 | 
					183450db46 | ||
| 
						 | 
					64959b980f | ||
| 
						 | 
					0b833e17ae | ||
| 
						 | 
					2e3c5543f2 | ||
| 
						 | 
					0475398a17 | ||
| 
						 | 
					bf483a2e94 | ||
| 
						 | 
					5e3539d6bb | ||
| 
						 | 
					a90265dd2a | ||
| 
						 | 
					8498ed9df4 | ||
| 
						 | 
					4169b0a6b7 | ||
| 
						 | 
					024339c99b | ||
| 
						 | 
					044697c438 | ||
| 
						 | 
					2aebeeb966 | ||
| 
						 | 
					4c79fe46c9 | ||
| 
						 | 
					51abafea26 | ||
| 
						 | 
					f1f1117b08 | ||
| 
						 | 
					5bb12ac1db | ||
| 
						 | 
					dbd9a05e10 | ||
| 
						 | 
					8981885196 | ||
| 
						 | 
					a7390c070f | ||
| 
						 | 
					4d0f895cda | ||
| 
						 | 
					48162219ac | ||
| 
						 | 
					1cb4a49ad3 | ||
| 
						 | 
					18ffbbd42d | ||
| 
						 | 
					7f1c0e55d6 | ||
| 
						 | 
					1876ffcd71 | ||
| 
						 | 
					3ef1e4269b | ||
| 
						 | 
					ae25e0f058 | ||
| 
						 | 
					7d634c1e62 | ||
| 
						 | 
					f713208d89 | ||
| 
						 | 
					5d98799205 | ||
| 
						 | 
					9086c678a9 | ||
| 
						 | 
					e434563e2e | ||
| 
						 | 
					29300fc972 | ||
| 
						 | 
					a88aceb846 | ||
| 
						 | 
					9986346d60 | ||
| 
						 | 
					7c642783f4 | ||
| 
						 | 
					0f1a2324bc | ||
| 
						 | 
					2d570267de | ||
| 
						 | 
					97849b6e70 | ||
| 
						 | 
					5d2a7d651e | ||
| 
						 | 
					b26477a21e | ||
| 
						 | 
					422a99bb26 | ||
| 
						 | 
					b447fc54c8 | ||
| 
						 | 
					9920037d13 | ||
| 
						 | 
					714e12f44f | ||
| 
						 | 
					fba91487ec | ||
| 
						 | 
					49ea5cc152 | ||
| 
						 | 
					87e0c25a34 | ||
| 
						 | 
					a648249b90 | ||
| 
						 | 
					083ab7b054 | ||
| 
						 | 
					b6ef337e45 | ||
| 
						 | 
					e62efa2a90 | ||
| 
						 | 
					3867945b46 | ||
| 
						 | 
					084896189d | ||
| 
						 | 
					e9033898da | ||
| 
						 | 
					d952e9de1a | ||
| 
						 | 
					bc3c9e1534 | ||
| 
						 | 
					6d137e44dc | ||
| 
						 | 
					6778521f12 | ||
| 
						 | 
					b0f3ef4969 | ||
| 
						 | 
					c01ef3f155 | ||
| 
						 | 
					241f22bab7 | ||
| 
						 | 
					a387a7aadf | ||
| 
						 | 
					d5e137de82 | ||
| 
						 | 
					e0cdd65352 | ||
| 
						 | 
					2c1ccdbc84 | ||
| 
						 | 
					36204d2ce9 | ||
| 
						 | 
					4639c1c351 | ||
| 
						 | 
					c863bd25e0 | ||
| 
						 | 
					7dcc682e29 | ||
| 
						 | 
					162875616d | ||
| 
						 | 
					9847025099 | ||
| 
						 | 
					bb183ea09a | ||
| 
						 | 
					9fd45e7aa7 | ||
| 
						 | 
					924b05ea9f | ||
| 
						 | 
					a0a0916108 | ||
| 
						 | 
					c78b9aedac | ||
| 
						 | 
					b07087f10b | ||
| 
						 | 
					f89c88dbb8 | ||
| 
						 | 
					0c76153b2c | ||
| 
						 | 
					70a23bcca3 | ||
| 
						 | 
					a66ef800bc | ||
| 
						 | 
					5f5f60a024 | ||
| 
						 | 
					d58399d417 | ||
| 
						 | 
					a1e9091f1e | ||
| 
						 | 
					84acebeb7f | ||
| 
						 | 
					83ba11d505 | ||
| 
						 | 
					97469a1c02 | ||
| 
						 | 
					c74dc6baf2 | ||
| 
						 | 
					1d90ae7c25 | ||
| 
						 | 
					39db7381b2 | ||
| 
						 | 
					ec8eaf0d73 | ||
| 
						 | 
					9d1a23a811 | ||
| 
						 | 
					421cbb9abd | ||
| 
						 | 
					bba9119a75 | ||
| 
						 | 
					6f00f296c8 | ||
| 
						 | 
					aee4719df4 | ||
| 
						 | 
					5f2402479d | ||
| 
						 | 
					c3fa4fb71c | ||
| 
						 | 
					a8d0d763b0 | ||
| 
						 | 
					6310b2544c | ||
| 
						 | 
					3c0ef8f8d3 | ||
| 
						 | 
					ebe39e9320 | ||
| 
						 | 
					1b8da176bf | ||
| 
						 | 
					66dd0de019 | ||
| 
						 | 
					a2be55fbc9 | ||
| 
						 | 
					41c24094eb | ||
| 
						 | 
					3b446f144f | ||
| 
						 | 
					760a9aad83 | ||
| 
						 | 
					c73372297c | ||
| 
						 | 
					1f331bf8d8 | ||
| 
						 | 
					57c78c56ba | ||
| 
						 | 
					1adb410e8e | ||
| 
						 | 
					6444ad4393 | ||
| 
						 | 
					1e7462606c | ||
| 
						 | 
					68aecfe8a2 | ||
| 
						 | 
					8ffe1838d4 | ||
| 
						 | 
					30becab0b6 | ||
| 
						 | 
					e037ae3d3a | ||
| 
						 | 
					2f3c79837e | ||
| 
						 | 
					4550d2bfba | ||
| 
						 | 
					5edaad41af | ||
| 
						 | 
					24447969be | ||
| 
						 | 
					c76e485d01 | ||
| 
						 | 
					021bee8dd4 | ||
| 
						 | 
					6c5945f9e8 | ||
| 
						 | 
					96ad348fb0 | ||
| 
						 | 
					72f33d8f3b | ||
| 
						 | 
					96f2a65f64 | ||
| 
						 | 
					43fe49e756 | ||
| 
						 | 
					e14ca4c438 | ||
| 
						 | 
					ed00018adc | ||
| 
						 | 
					f7aef49509 | ||
| 
						 | 
					e1edd43205 | ||
| 
						 | 
					03d253ae20 | ||
| 
						 | 
					577b047cc1 | ||
| 
						 | 
					c46d894192 | ||
| 
						 | 
					e4480569f7 | ||
| 
						 | 
					e6e6360c1a | ||
| 
						 | 
					6b4236343a | ||
| 
						 | 
					45abab3a78 | ||
| 
						 | 
					4d7492a00d | ||
| 
						 | 
					b83af2d54a | ||
| 
						 | 
					10b73525be | ||
| 
						 | 
					89dbb48860 | ||
| 
						 | 
					16854fff1a | ||
| 
						 | 
					b34f59eafc | ||
| 
						 | 
					38cb371c72 | ||
| 
						 | 
					5199c5e073 | ||
| 
						 | 
					3ef72d28bd | ||
| 
						 | 
					06906a8bd3 | ||
| 
						 | 
					0a3c728a96 | ||
| 
						 | 
					37745cb5b2 | ||
| 
						 | 
					269eac9a15 | ||
| 
						 | 
					d82461ea70 | ||
| 
						 | 
					e9d2dfe329 | ||
| 
						 | 
					9d052dc86c | ||
| 
						 | 
					ad5d28bc48 | ||
| 
						 | 
					2b5a8ebf2f | ||
| 
						 | 
					28999a242c | ||
| 
						 | 
					186821f971 | ||
| 
						 | 
					1c8e4c346b | ||
| 
						 | 
					6bcd037264 | ||
| 
						 | 
					439e2d4934 | ||
| 
						 | 
					f812774637 | ||
| 
						 | 
					d82693c2d3 | ||
| 
						 | 
					907cc19a41 | ||
| 
						 | 
					cc83ea8eb6 | ||
| 
						 | 
					527e0c916f | ||
| 
						 | 
					fb5d94bbef | ||
| 
						 | 
					4fe3ec8a08 | ||
| 
						 | 
					937b67cbc0 | ||
| 
						 | 
					4e50087612 | 
@@ -17,7 +17,7 @@ Loop: xrpld.app xrpld.rpc
 | 
			
		||||
  xrpld.rpc > xrpld.app
 | 
			
		||||
 | 
			
		||||
Loop: xrpld.app xrpld.shamap
 | 
			
		||||
  xrpld.app > xrpld.shamap
 | 
			
		||||
  xrpld.shamap ~= xrpld.app
 | 
			
		||||
 | 
			
		||||
Loop: xrpld.core xrpld.perflog
 | 
			
		||||
  xrpld.perflog == xrpld.core
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,10 @@ libxrpl.ledger > xrpl.ledger
 | 
			
		||||
libxrpl.ledger > xrpl.protocol
 | 
			
		||||
libxrpl.net > xrpl.basics
 | 
			
		||||
libxrpl.net > xrpl.net
 | 
			
		||||
libxrpl.nodestore > xrpl.basics
 | 
			
		||||
libxrpl.nodestore > xrpl.json
 | 
			
		||||
libxrpl.nodestore > xrpl.nodestore
 | 
			
		||||
libxrpl.nodestore > xrpl.protocol
 | 
			
		||||
libxrpl.protocol > xrpl.basics
 | 
			
		||||
libxrpl.protocol > xrpl.json
 | 
			
		||||
libxrpl.protocol > xrpl.protocol
 | 
			
		||||
@@ -18,6 +22,9 @@ libxrpl.server > xrpl.basics
 | 
			
		||||
libxrpl.server > xrpl.json
 | 
			
		||||
libxrpl.server > xrpl.protocol
 | 
			
		||||
libxrpl.server > xrpl.server
 | 
			
		||||
libxrpl.shamap > xrpl.basics
 | 
			
		||||
libxrpl.shamap > xrpl.protocol
 | 
			
		||||
libxrpl.shamap > xrpl.shamap
 | 
			
		||||
test.app > test.jtx
 | 
			
		||||
test.app > test.rpc
 | 
			
		||||
test.app > test.toplevel
 | 
			
		||||
@@ -25,11 +32,11 @@ test.app > test.unit_test
 | 
			
		||||
test.app > xrpl.basics
 | 
			
		||||
test.app > xrpld.app
 | 
			
		||||
test.app > xrpld.core
 | 
			
		||||
test.app > xrpld.nodestore
 | 
			
		||||
test.app > xrpld.overlay
 | 
			
		||||
test.app > xrpld.rpc
 | 
			
		||||
test.app > xrpl.json
 | 
			
		||||
test.app > xrpl.ledger
 | 
			
		||||
test.app > xrpl.nodestore
 | 
			
		||||
test.app > xrpl.protocol
 | 
			
		||||
test.app > xrpl.resource
 | 
			
		||||
test.basics > test.jtx
 | 
			
		||||
@@ -86,8 +93,7 @@ test.nodestore > test.toplevel
 | 
			
		||||
test.nodestore > test.unit_test
 | 
			
		||||
test.nodestore > xrpl.basics
 | 
			
		||||
test.nodestore > xrpld.core
 | 
			
		||||
test.nodestore > xrpld.nodestore
 | 
			
		||||
test.nodestore > xrpld.unity
 | 
			
		||||
test.nodestore > xrpl.nodestore
 | 
			
		||||
test.overlay > test.jtx
 | 
			
		||||
test.overlay > test.toplevel
 | 
			
		||||
test.overlay > test.unit_test
 | 
			
		||||
@@ -95,8 +101,8 @@ test.overlay > xrpl.basics
 | 
			
		||||
test.overlay > xrpld.app
 | 
			
		||||
test.overlay > xrpld.overlay
 | 
			
		||||
test.overlay > xrpld.peerfinder
 | 
			
		||||
test.overlay > xrpld.shamap
 | 
			
		||||
test.overlay > xrpl.protocol
 | 
			
		||||
test.overlay > xrpl.shamap
 | 
			
		||||
test.peerfinder > test.beast
 | 
			
		||||
test.peerfinder > test.unit_test
 | 
			
		||||
test.peerfinder > xrpl.basics
 | 
			
		||||
@@ -131,9 +137,9 @@ test.server > xrpl.json
 | 
			
		||||
test.server > xrpl.server
 | 
			
		||||
test.shamap > test.unit_test
 | 
			
		||||
test.shamap > xrpl.basics
 | 
			
		||||
test.shamap > xrpld.nodestore
 | 
			
		||||
test.shamap > xrpld.shamap
 | 
			
		||||
test.shamap > xrpl.nodestore
 | 
			
		||||
test.shamap > xrpl.protocol
 | 
			
		||||
test.shamap > xrpl.shamap
 | 
			
		||||
test.toplevel > test.csf
 | 
			
		||||
test.toplevel > xrpl.json
 | 
			
		||||
test.unit_test > xrpl.basics
 | 
			
		||||
@@ -144,6 +150,8 @@ xrpl.json > xrpl.basics
 | 
			
		||||
xrpl.ledger > xrpl.basics
 | 
			
		||||
xrpl.ledger > xrpl.protocol
 | 
			
		||||
xrpl.net > xrpl.basics
 | 
			
		||||
xrpl.nodestore > xrpl.basics
 | 
			
		||||
xrpl.nodestore > xrpl.protocol
 | 
			
		||||
xrpl.protocol > xrpl.basics
 | 
			
		||||
xrpl.protocol > xrpl.json
 | 
			
		||||
xrpl.resource > xrpl.basics
 | 
			
		||||
@@ -152,17 +160,21 @@ xrpl.resource > xrpl.protocol
 | 
			
		||||
xrpl.server > xrpl.basics
 | 
			
		||||
xrpl.server > xrpl.json
 | 
			
		||||
xrpl.server > xrpl.protocol
 | 
			
		||||
xrpl.shamap > xrpl.basics
 | 
			
		||||
xrpl.shamap > xrpl.nodestore
 | 
			
		||||
xrpl.shamap > xrpl.protocol
 | 
			
		||||
xrpld.app > test.unit_test
 | 
			
		||||
xrpld.app > xrpl.basics
 | 
			
		||||
xrpld.app > xrpld.conditions
 | 
			
		||||
xrpld.app > xrpld.consensus
 | 
			
		||||
xrpld.app > xrpld.nodestore
 | 
			
		||||
xrpld.app > xrpld.perflog
 | 
			
		||||
xrpld.app > xrpl.json
 | 
			
		||||
xrpld.app > xrpl.ledger
 | 
			
		||||
xrpld.app > xrpl.net
 | 
			
		||||
xrpld.app > xrpl.nodestore
 | 
			
		||||
xrpld.app > xrpl.protocol
 | 
			
		||||
xrpld.app > xrpl.resource
 | 
			
		||||
xrpld.app > xrpl.shamap
 | 
			
		||||
xrpld.conditions > xrpl.basics
 | 
			
		||||
xrpld.conditions > xrpl.protocol
 | 
			
		||||
xrpld.consensus > xrpl.basics
 | 
			
		||||
@@ -172,11 +184,6 @@ xrpld.core > xrpl.basics
 | 
			
		||||
xrpld.core > xrpl.json
 | 
			
		||||
xrpld.core > xrpl.net
 | 
			
		||||
xrpld.core > xrpl.protocol
 | 
			
		||||
xrpld.nodestore > xrpl.basics
 | 
			
		||||
xrpld.nodestore > xrpld.core
 | 
			
		||||
xrpld.nodestore > xrpld.unity
 | 
			
		||||
xrpld.nodestore > xrpl.json
 | 
			
		||||
xrpld.nodestore > xrpl.protocol
 | 
			
		||||
xrpld.overlay > xrpl.basics
 | 
			
		||||
xrpld.overlay > xrpld.core
 | 
			
		||||
xrpld.overlay > xrpld.peerfinder
 | 
			
		||||
@@ -192,13 +199,11 @@ xrpld.perflog > xrpl.basics
 | 
			
		||||
xrpld.perflog > xrpl.json
 | 
			
		||||
xrpld.rpc > xrpl.basics
 | 
			
		||||
xrpld.rpc > xrpld.core
 | 
			
		||||
xrpld.rpc > xrpld.nodestore
 | 
			
		||||
xrpld.rpc > xrpl.json
 | 
			
		||||
xrpld.rpc > xrpl.ledger
 | 
			
		||||
xrpld.rpc > xrpl.net
 | 
			
		||||
xrpld.rpc > xrpl.nodestore
 | 
			
		||||
xrpld.rpc > xrpl.protocol
 | 
			
		||||
xrpld.rpc > xrpl.resource
 | 
			
		||||
xrpld.rpc > xrpl.server
 | 
			
		||||
xrpld.shamap > xrpl.basics
 | 
			
		||||
xrpld.shamap > xrpld.nodestore
 | 
			
		||||
xrpld.shamap > xrpl.protocol
 | 
			
		||||
xrpld.shamap > xrpl.shamap
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								.github/scripts/strategy-matrix/linux.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										48
									
								
								.github/scripts/strategy-matrix/linux.json
									
									
									
									
										vendored
									
									
								
							@@ -15,168 +15,168 @@
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "12",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "13",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "14",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "15",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "16",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "17",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "18",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "19",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "debian",
 | 
			
		||||
      "distro_version": "bookworm",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "20",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "8",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "14",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "8",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "any",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "9",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "12",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "9",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "13",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "9",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "14",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "9",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "any",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "10",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "14",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "rhel",
 | 
			
		||||
      "distro_version": "10",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "any",
 | 
			
		||||
      "image_sha": "10e69b4"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "ubuntu",
 | 
			
		||||
      "distro_version": "jammy",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "12",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "ubuntu",
 | 
			
		||||
      "distro_version": "noble",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "13",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "ubuntu",
 | 
			
		||||
      "distro_version": "noble",
 | 
			
		||||
      "compiler_name": "gcc",
 | 
			
		||||
      "compiler_version": "14",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "ubuntu",
 | 
			
		||||
      "distro_version": "noble",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "16",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "ubuntu",
 | 
			
		||||
      "distro_version": "noble",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "17",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "ubuntu",
 | 
			
		||||
      "distro_version": "noble",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "18",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "distro_name": "ubuntu",
 | 
			
		||||
      "distro_version": "noble",
 | 
			
		||||
      "compiler_name": "clang",
 | 
			
		||||
      "compiler_version": "19",
 | 
			
		||||
      "image_sha": "6948666"
 | 
			
		||||
      "image_sha": "97ba375"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "build_type": ["Debug", "Release"],
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ if (static OR APPLE OR MSVC)
 | 
			
		||||
  set (OPENSSL_USE_STATIC_LIBS ON)
 | 
			
		||||
endif ()
 | 
			
		||||
set (OPENSSL_MSVC_STATIC_RT ON)
 | 
			
		||||
find_dependency (OpenSSL 1.1.1 REQUIRED)
 | 
			
		||||
find_dependency (OpenSSL REQUIRED)
 | 
			
		||||
find_dependency (ZLIB)
 | 
			
		||||
find_dependency (date)
 | 
			
		||||
if (TARGET ZLIB::ZLIB)
 | 
			
		||||
 
 | 
			
		||||
@@ -53,14 +53,15 @@ add_library(xrpl.imports.main INTERFACE)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(xrpl.imports.main
 | 
			
		||||
  INTERFACE
 | 
			
		||||
    LibArchive::LibArchive
 | 
			
		||||
    OpenSSL::Crypto
 | 
			
		||||
    Ripple::boost
 | 
			
		||||
    Ripple::opts
 | 
			
		||||
    Ripple::syslibs
 | 
			
		||||
    absl::random_random
 | 
			
		||||
    date::date
 | 
			
		||||
    ed25519::ed25519
 | 
			
		||||
    LibArchive::LibArchive
 | 
			
		||||
    OpenSSL::Crypto
 | 
			
		||||
    Ripple::boost
 | 
			
		||||
    Ripple::libs
 | 
			
		||||
    Ripple::opts
 | 
			
		||||
    Ripple::syslibs
 | 
			
		||||
    secp256k1::secp256k1
 | 
			
		||||
    xrpl.libpb
 | 
			
		||||
    xxHash::xxhash
 | 
			
		||||
@@ -111,6 +112,21 @@ target_link_libraries(xrpl.libxrpl.net PUBLIC
 | 
			
		||||
add_module(xrpl server)
 | 
			
		||||
target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol)
 | 
			
		||||
 | 
			
		||||
add_module(xrpl nodestore)
 | 
			
		||||
target_link_libraries(xrpl.libxrpl.nodestore PUBLIC
 | 
			
		||||
        xrpl.libxrpl.basics
 | 
			
		||||
        xrpl.libxrpl.json
 | 
			
		||||
        xrpl.libxrpl.protocol
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_module(xrpl shamap)
 | 
			
		||||
target_link_libraries(xrpl.libxrpl.shamap PUBLIC
 | 
			
		||||
        xrpl.libxrpl.basics
 | 
			
		||||
        xrpl.libxrpl.crypto
 | 
			
		||||
        xrpl.libxrpl.protocol
 | 
			
		||||
        xrpl.libxrpl.nodestore
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_module(xrpl ledger)
 | 
			
		||||
target_link_libraries(xrpl.libxrpl.ledger PUBLIC
 | 
			
		||||
  xrpl.libxrpl.basics
 | 
			
		||||
@@ -136,6 +152,8 @@ target_link_modules(xrpl PUBLIC
 | 
			
		||||
  protocol
 | 
			
		||||
  resource
 | 
			
		||||
  server
 | 
			
		||||
  nodestore
 | 
			
		||||
  shamap
 | 
			
		||||
  net
 | 
			
		||||
  ledger
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,20 +8,23 @@ install (
 | 
			
		||||
  TARGETS
 | 
			
		||||
    common
 | 
			
		||||
    opts
 | 
			
		||||
    ripple_syslibs
 | 
			
		||||
    ripple_boost
 | 
			
		||||
    ripple_libs
 | 
			
		||||
    ripple_syslibs
 | 
			
		||||
    xrpl.imports.main
 | 
			
		||||
    xrpl.libpb
 | 
			
		||||
    xrpl.libxrpl
 | 
			
		||||
    xrpl.libxrpl.basics
 | 
			
		||||
    xrpl.libxrpl.beast
 | 
			
		||||
    xrpl.libxrpl.crypto
 | 
			
		||||
    xrpl.libxrpl.json
 | 
			
		||||
    xrpl.libxrpl.ledger
 | 
			
		||||
    xrpl.libxrpl.net
 | 
			
		||||
    xrpl.libxrpl.nodestore
 | 
			
		||||
    xrpl.libxrpl.protocol
 | 
			
		||||
    xrpl.libxrpl.resource
 | 
			
		||||
    xrpl.libxrpl.ledger
 | 
			
		||||
    xrpl.libxrpl.server
 | 
			
		||||
    xrpl.libxrpl.net
 | 
			
		||||
    xrpl.libxrpl
 | 
			
		||||
    xrpl.libxrpl.shamap
 | 
			
		||||
    antithesis-sdk-cpp
 | 
			
		||||
  EXPORT RippleExports
 | 
			
		||||
  LIBRARY DESTINATION lib
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,8 @@ public:
 | 
			
		||||
    explicit Number(rep mantissa, int exponent);
 | 
			
		||||
    explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept;
 | 
			
		||||
 | 
			
		||||
    static Number const zero;
 | 
			
		||||
 | 
			
		||||
    constexpr rep
 | 
			
		||||
    mantissa() const noexcept;
 | 
			
		||||
    constexpr int
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
// The duplication is because Visual Studio 2019 cannot compile that header
 | 
			
		||||
// even with the option -Zc:__cplusplus added.
 | 
			
		||||
#define ALWAYS(cond, message, ...) assert((message) && (cond))
 | 
			
		||||
#define ALWAYS_OR_UNREACHABLE(cond, message, ...) assert((message) && (cond))
 | 
			
		||||
#define ALWAYS_OR_UNREACHABLE(cond, message) assert((message) && (cond))
 | 
			
		||||
#define SOMETIMES(cond, message, ...)
 | 
			
		||||
#define REACHABLE(message, ...)
 | 
			
		||||
#define UNREACHABLE(message, ...) assert((message) && false)
 | 
			
		||||
 
 | 
			
		||||
@@ -395,6 +395,9 @@ public:
 | 
			
		||||
    /// Return true if the object has a member named key.
 | 
			
		||||
    bool
 | 
			
		||||
    isMember(std::string const& key) const;
 | 
			
		||||
    /// Return true if the object has a member named key.
 | 
			
		||||
    bool
 | 
			
		||||
    isMember(StaticString const& key) const;
 | 
			
		||||
 | 
			
		||||
    /// \brief Return a list of the member names.
 | 
			
		||||
    ///
 | 
			
		||||
 
 | 
			
		||||
@@ -387,6 +387,45 @@ public:
 | 
			
		||||
    emptyDirDelete(Keylet const& directory);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace directory {
 | 
			
		||||
/** Helper functions for managing low-level directory operations.
 | 
			
		||||
    These are not part of the ApplyView interface.
 | 
			
		||||
 | 
			
		||||
    Don't use them unless you really, really know what you're doing.
 | 
			
		||||
    Instead use dirAdd, dirInsert, etc.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
std::uint64_t
 | 
			
		||||
createRoot(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    Keylet const& directory,
 | 
			
		||||
    uint256 const& key,
 | 
			
		||||
    std::function<void(std::shared_ptr<SLE> const&)> const& describe);
 | 
			
		||||
 | 
			
		||||
auto
 | 
			
		||||
findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start);
 | 
			
		||||
 | 
			
		||||
std::uint64_t
 | 
			
		||||
insertKey(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    SLE::ref node,
 | 
			
		||||
    std::uint64_t page,
 | 
			
		||||
    bool preserveOrder,
 | 
			
		||||
    STVector256& indexes,
 | 
			
		||||
    uint256 const& key);
 | 
			
		||||
 | 
			
		||||
std::optional<std::uint64_t>
 | 
			
		||||
insertPage(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    std::uint64_t page,
 | 
			
		||||
    SLE::pointer node,
 | 
			
		||||
    std::uint64_t nextPage,
 | 
			
		||||
    SLE::ref next,
 | 
			
		||||
    uint256 const& key,
 | 
			
		||||
    Keylet const& directory,
 | 
			
		||||
    std::function<void(std::shared_ptr<SLE> const&)> const& describe);
 | 
			
		||||
 | 
			
		||||
}  // namespace directory
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -242,6 +242,80 @@ isDeepFrozen(
 | 
			
		||||
    Currency const& currency,
 | 
			
		||||
    AccountID const& issuer);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] inline bool
 | 
			
		||||
isDeepFrozen(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Issue const& issue,
 | 
			
		||||
    int = 0 /*ignored*/)
 | 
			
		||||
{
 | 
			
		||||
    return isDeepFrozen(view, account, issue.currency, issue.account);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] inline bool
 | 
			
		||||
isDeepFrozen(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    MPTIssue const& mptIssue,
 | 
			
		||||
    int depth = 0)
 | 
			
		||||
{
 | 
			
		||||
    // Unlike IOUs, frozen / locked MPTs are not allowed to send or receive
 | 
			
		||||
    // funds, so checking "deep frozen" is the same as checking "frozen".
 | 
			
		||||
    return isFrozen(view, account, mptIssue, depth);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *   isFrozen check is recursive for MPT shares in a vault, descending to
 | 
			
		||||
 *   assets in the vault, up to maxAssetCheckDepth recursion depth. This is
 | 
			
		||||
 *   purely defensive, as we currently do not allow such vaults to be created.
 | 
			
		||||
 */
 | 
			
		||||
[[nodiscard]] inline bool
 | 
			
		||||
isDeepFrozen(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    int depth = 0)
 | 
			
		||||
{
 | 
			
		||||
    return std::visit(
 | 
			
		||||
        [&](auto const& issue) {
 | 
			
		||||
            return isDeepFrozen(view, account, issue, depth);
 | 
			
		||||
        },
 | 
			
		||||
        asset.value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] inline TER
 | 
			
		||||
checkDeepFrozen(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Issue const& issue)
 | 
			
		||||
{
 | 
			
		||||
    return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN
 | 
			
		||||
                                              : (TER)tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] inline TER
 | 
			
		||||
checkDeepFrozen(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    MPTIssue const& mptIssue)
 | 
			
		||||
{
 | 
			
		||||
    return isDeepFrozen(view, account, mptIssue) ? (TER)tecLOCKED
 | 
			
		||||
                                                 : (TER)tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] inline TER
 | 
			
		||||
checkDeepFrozen(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Asset const& asset)
 | 
			
		||||
{
 | 
			
		||||
    return std::visit(
 | 
			
		||||
        [&](auto const& issue) {
 | 
			
		||||
            return checkDeepFrozen(view, account, issue);
 | 
			
		||||
        },
 | 
			
		||||
        asset.value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] bool
 | 
			
		||||
isLPTokenFrozen(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
@@ -287,6 +361,49 @@ accountHolds(
 | 
			
		||||
    AuthHandling zeroIfUnauthorized,
 | 
			
		||||
    beast::Journal j);
 | 
			
		||||
 | 
			
		||||
// Returns the amount an account can spend total.
 | 
			
		||||
//
 | 
			
		||||
// These functions use accountHolds, but unlike accountHolds:
 | 
			
		||||
// * The account can go into debt.
 | 
			
		||||
// * If the account is the asset issuer the only limit is defined by the asset /
 | 
			
		||||
//   issuance.
 | 
			
		||||
//
 | 
			
		||||
// <-- saAmount: amount of currency held by account. May be negative.
 | 
			
		||||
[[nodiscard]] STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Currency const& currency,
 | 
			
		||||
    AccountID const& issuer,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    beast::Journal j);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Issue const& issue,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    beast::Journal j);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    MPTIssue const& mptIssue,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    AuthHandling zeroIfUnauthorized,
 | 
			
		||||
    beast::Journal j);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    AuthHandling zeroIfUnauthorized,
 | 
			
		||||
    beast::Journal j);
 | 
			
		||||
 | 
			
		||||
// Returns the amount an account can spend of the currency type saDefault, or
 | 
			
		||||
// returns saDefault if this account is the issuer of the currency in
 | 
			
		||||
// question. Should be used in favor of accountHolds when questioning how much
 | 
			
		||||
@@ -533,7 +650,11 @@ dirNext(
 | 
			
		||||
describeOwnerDir(AccountID const& account);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr<SLE>& object);
 | 
			
		||||
dirLink(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& owner,
 | 
			
		||||
    std::shared_ptr<SLE>& object,
 | 
			
		||||
    SF_UINT64 const& node = sfOwnerNode);
 | 
			
		||||
 | 
			
		||||
AccountID
 | 
			
		||||
pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
 | 
			
		||||
@@ -573,7 +694,7 @@ isPseudoAccount(std::shared_ptr<SLE const> sleAcct);
 | 
			
		||||
getPseudoAccountFields();
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] inline bool
 | 
			
		||||
isPseudoAccount(ReadView const& view, AccountID accountId)
 | 
			
		||||
isPseudoAccount(ReadView const& view, AccountID const& accountId)
 | 
			
		||||
{
 | 
			
		||||
    return isPseudoAccount(view.read(keylet::account(accountId)));
 | 
			
		||||
}
 | 
			
		||||
@@ -581,6 +702,68 @@ isPseudoAccount(ReadView const& view, AccountID accountId)
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
canAddHolding(ReadView const& view, Asset const& asset);
 | 
			
		||||
 | 
			
		||||
/** Validates that the destination SLE and tag are valid
 | 
			
		||||
 | 
			
		||||
   - Checks that the SLE is not null.
 | 
			
		||||
   - If the SLE requires a destination tag, checks that there is a tag.
 | 
			
		||||
*/
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);
 | 
			
		||||
 | 
			
		||||
/** Checks that can withdraw funds from an object to itself or a destination.
 | 
			
		||||
 *
 | 
			
		||||
 * The receiver may be either the submitting account (sfAccount) or a different
 | 
			
		||||
 * destination account (sfDestination).
 | 
			
		||||
 *
 | 
			
		||||
 *    - Checks that the receiver account exists.
 | 
			
		||||
 *    - If the receiver requires a destination tag, check that one exists, even
 | 
			
		||||
 *      if withdrawing to self.
 | 
			
		||||
 *    - If withdrawing to self, succeed.
 | 
			
		||||
 *    - If not, checks if the receiver requires deposit authorization, and if
 | 
			
		||||
 *      the sender has it.
 | 
			
		||||
 */
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
canWithdraw(
 | 
			
		||||
    AccountID const& from,
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& to,
 | 
			
		||||
    SLE::const_ref toSle,
 | 
			
		||||
    bool hasDestinationTag);
 | 
			
		||||
 | 
			
		||||
/** Checks that can withdraw funds from an object to itself or a destination.
 | 
			
		||||
 *
 | 
			
		||||
 * The receiver may be either the submitting account (sfAccount) or a different
 | 
			
		||||
 * destination account (sfDestination).
 | 
			
		||||
 *
 | 
			
		||||
 *    - Checks that the receiver account exists.
 | 
			
		||||
 *    - If the receiver requires a destination tag, check that one exists, even
 | 
			
		||||
 *      if withdrawing to self.
 | 
			
		||||
 *    - If withdrawing to self, succeed.
 | 
			
		||||
 *    - If not, checks if the receiver requires deposit authorization, and if
 | 
			
		||||
 *      the sender has it.
 | 
			
		||||
 */
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
canWithdraw(
 | 
			
		||||
    AccountID const& from,
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& to,
 | 
			
		||||
    bool hasDestinationTag);
 | 
			
		||||
 | 
			
		||||
/** Checks that can withdraw funds from an object to itself or a destination.
 | 
			
		||||
 *
 | 
			
		||||
 * The receiver may be either the submitting account (sfAccount) or a different
 | 
			
		||||
 * destination account (sfDestination).
 | 
			
		||||
 *
 | 
			
		||||
 *    - Checks that the receiver account exists.
 | 
			
		||||
 *    - If the receiver requires a destination tag, check that one exists, even
 | 
			
		||||
 *      if withdrawing to self.
 | 
			
		||||
 *    - If withdrawing to self, succeed.
 | 
			
		||||
 *    - If not, checks if the receiver requires deposit authorization, and if
 | 
			
		||||
 *      the sender has it.
 | 
			
		||||
 */
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
canWithdraw(ReadView const& view, STTx const& tx);
 | 
			
		||||
 | 
			
		||||
/// Any transactors that call addEmptyHolding() in doApply must call
 | 
			
		||||
/// canAddHolding() in preflight with the same View and Asset
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
@@ -750,6 +933,22 @@ accountSend(
 | 
			
		||||
    beast::Journal j,
 | 
			
		||||
    WaiveTransferFee waiveFee = WaiveTransferFee::No);
 | 
			
		||||
 | 
			
		||||
using MultiplePaymentDestinations = std::vector<std::pair<AccountID, Number>>;
 | 
			
		||||
/** Like accountSend, except one account is sending multiple payments (with the
 | 
			
		||||
 *  same asset!) simultaneously
 | 
			
		||||
 *
 | 
			
		||||
 * Calls static accountSendMultiIOU if saAmount represents Issue.
 | 
			
		||||
 * Calls static accountSendMultiMPT if saAmount represents MPTIssue.
 | 
			
		||||
 */
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
accountSendMulti(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& senderID,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    MultiplePaymentDestinations const& receivers,
 | 
			
		||||
    beast::Journal j,
 | 
			
		||||
    WaiveTransferFee waiveFee = WaiveTransferFee::No);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
issueIOU(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_BACKEND_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_BACKEND_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Types.h>
 | 
			
		||||
#include <xrpl/nodestore/Types.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
@@ -20,13 +20,12 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_DATABASE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_DATABASE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Backend.h>
 | 
			
		||||
#include <xrpld/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpld/nodestore/Scheduler.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/BasicConfig.h>
 | 
			
		||||
#include <xrpl/basics/Log.h>
 | 
			
		||||
#include <xrpl/basics/TaggedCache.ipp>
 | 
			
		||||
#include <xrpl/nodestore/Backend.h>
 | 
			
		||||
#include <xrpl/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpl/nodestore/Scheduler.h>
 | 
			
		||||
#include <xrpl/protocol/SystemParameters.h>
 | 
			
		||||
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_DATABASEROTATING_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_DATABASEROTATING_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Database.h>
 | 
			
		||||
#include <xrpl/nodestore/Database.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_DUMMYSCHEDULER_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_DUMMYSCHEDULER_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Scheduler.h>
 | 
			
		||||
#include <xrpl/nodestore/Scheduler.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -20,11 +20,10 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_FACTORY_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_FACTORY_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Backend.h>
 | 
			
		||||
#include <xrpld/nodestore/Scheduler.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/BasicConfig.h>
 | 
			
		||||
#include <xrpl/beast/utility/Journal.h>
 | 
			
		||||
#include <xrpl/nodestore/Backend.h>
 | 
			
		||||
#include <xrpl/nodestore/Scheduler.h>
 | 
			
		||||
 | 
			
		||||
#include <nudb/store.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -20,8 +20,8 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_MANAGER_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_MANAGER_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/DatabaseRotating.h>
 | 
			
		||||
#include <xrpld/nodestore/Factory.h>
 | 
			
		||||
#include <xrpl/nodestore/DatabaseRotating.h>
 | 
			
		||||
#include <xrpl/nodestore/Factory.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_SCHEDULER_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_SCHEDULER_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Task.h>
 | 
			
		||||
#include <xrpl/nodestore/Task.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_TYPES_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_TYPES_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpl/nodestore/NodeObject.h>
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -20,9 +20,9 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_BATCHWRITER_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_BATCHWRITER_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Scheduler.h>
 | 
			
		||||
#include <xrpld/nodestore/Task.h>
 | 
			
		||||
#include <xrpld/nodestore/Types.h>
 | 
			
		||||
#include <xrpl/nodestore/Scheduler.h>
 | 
			
		||||
#include <xrpl/nodestore/Task.h>
 | 
			
		||||
#include <xrpl/nodestore/Types.h>
 | 
			
		||||
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
@@ -20,10 +20,9 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_DATABASENODEIMP_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_DATABASENODEIMP_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Database.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/TaggedCache.h>
 | 
			
		||||
#include <xrpl/basics/chrono.h>
 | 
			
		||||
#include <xrpl/nodestore/Database.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_DATABASEROTATINGIMP_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_DATABASEROTATINGIMP_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/DatabaseRotating.h>
 | 
			
		||||
#include <xrpl/nodestore/DatabaseRotating.h>
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_DECODEDBLOB_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_DECODEDBLOB_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpl/nodestore/NodeObject.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -20,9 +20,8 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_ENCODEDBLOB_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_ENCODEDBLOB_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/NodeObject.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/beast/utility/instrumentation.h>
 | 
			
		||||
#include <xrpl/nodestore/NodeObject.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/align/align_up.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_NODESTORE_MANAGERIMP_H_INCLUDED
 | 
			
		||||
#define RIPPLE_NODESTORE_MANAGERIMP_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Manager.h>
 | 
			
		||||
#include <xrpl/nodestore/Manager.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +39,7 @@ public:
 | 
			
		||||
    static void
 | 
			
		||||
    missing_backend();
 | 
			
		||||
 | 
			
		||||
    ManagerImp() = default;
 | 
			
		||||
    ManagerImp();
 | 
			
		||||
 | 
			
		||||
    ~ManagerImp() = default;
 | 
			
		||||
 | 
			
		||||
@@ -23,11 +23,10 @@
 | 
			
		||||
// Disable lz4 deprecation warning due to incompatibility with clang attributes
 | 
			
		||||
#define LZ4_DISABLE_DEPRECATE_WARNINGS
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/varint.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/basics/safe_cast.h>
 | 
			
		||||
#include <xrpl/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/varint.h>
 | 
			
		||||
#include <xrpl/protocol/HashPrefix.h>
 | 
			
		||||
 | 
			
		||||
#include <nudb/detail/field.hpp>
 | 
			
		||||
@@ -20,6 +20,11 @@
 | 
			
		||||
#ifndef RIPPLE_PROTOCOL_APIVERSION_H_INCLUDED
 | 
			
		||||
#define RIPPLE_PROTOCOL_APIVERSION_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpl/beast/core/SemanticVersion.h>
 | 
			
		||||
#include <xrpl/beast/utility/instrumentation.h>
 | 
			
		||||
#include <xrpl/json/json_value.h>
 | 
			
		||||
#include <xrpl/protocol/jss.h>
 | 
			
		||||
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
@@ -72,6 +77,77 @@ static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion);
 | 
			
		||||
static_assert(apiBetaVersion >= apiMaximumSupportedVersion);
 | 
			
		||||
static_assert(apiMaximumValidVersion >= apiMaximumSupportedVersion);
 | 
			
		||||
 | 
			
		||||
template <class JsonObject>
 | 
			
		||||
void
 | 
			
		||||
setVersion(JsonObject& parent, unsigned int apiVersion, bool betaEnabled)
 | 
			
		||||
{
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
        apiVersion != apiInvalidVersion,
 | 
			
		||||
        "ripple::RPC::setVersion : input is valid");
 | 
			
		||||
    auto& retObj = addObject(parent, jss::version);
 | 
			
		||||
 | 
			
		||||
    if (apiVersion == apiVersionIfUnspecified)
 | 
			
		||||
    {
 | 
			
		||||
        // API version numbers used in API version 1
 | 
			
		||||
        static beast::SemanticVersion const firstVersion{"1.0.0"};
 | 
			
		||||
        static beast::SemanticVersion const goodVersion{"1.0.0"};
 | 
			
		||||
        static beast::SemanticVersion const lastVersion{"1.0.0"};
 | 
			
		||||
 | 
			
		||||
        retObj[jss::first] = firstVersion.print();
 | 
			
		||||
        retObj[jss::good] = goodVersion.print();
 | 
			
		||||
        retObj[jss::last] = lastVersion.print();
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        retObj[jss::first] = apiMinimumSupportedVersion.value;
 | 
			
		||||
        retObj[jss::last] =
 | 
			
		||||
            betaEnabled ? apiBetaVersion : apiMaximumSupportedVersion;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Retrieve the api version number from the json value
 | 
			
		||||
 *
 | 
			
		||||
 * Note that APIInvalidVersion will be returned if
 | 
			
		||||
 * 1) the version number field has a wrong format
 | 
			
		||||
 * 2) the version number retrieved is out of the supported range
 | 
			
		||||
 * 3) the version number is unspecified and
 | 
			
		||||
 *    APIVersionIfUnspecified is out of the supported range
 | 
			
		||||
 *
 | 
			
		||||
 * @param jv a Json value that may or may not specifies
 | 
			
		||||
 *        the api version number
 | 
			
		||||
 * @param betaEnabled if the beta API version is enabled
 | 
			
		||||
 * @return the api version number
 | 
			
		||||
 */
 | 
			
		||||
inline unsigned int
 | 
			
		||||
getAPIVersionNumber(Json::Value const& jv, bool betaEnabled)
 | 
			
		||||
{
 | 
			
		||||
    static Json::Value const minVersion(RPC::apiMinimumSupportedVersion);
 | 
			
		||||
    Json::Value const maxVersion(
 | 
			
		||||
        betaEnabled ? RPC::apiBetaVersion : RPC::apiMaximumSupportedVersion);
 | 
			
		||||
 | 
			
		||||
    if (jv.isObject())
 | 
			
		||||
    {
 | 
			
		||||
        if (jv.isMember(jss::api_version))
 | 
			
		||||
        {
 | 
			
		||||
            auto const specifiedVersion = jv[jss::api_version];
 | 
			
		||||
            if (!specifiedVersion.isInt() && !specifiedVersion.isUInt())
 | 
			
		||||
            {
 | 
			
		||||
                return RPC::apiInvalidVersion;
 | 
			
		||||
            }
 | 
			
		||||
            auto const specifiedVersionInt = specifiedVersion.asInt();
 | 
			
		||||
            if (specifiedVersionInt < minVersion ||
 | 
			
		||||
                specifiedVersionInt > maxVersion)
 | 
			
		||||
            {
 | 
			
		||||
                return RPC::apiInvalidVersion;
 | 
			
		||||
            }
 | 
			
		||||
            return specifiedVersionInt;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return RPC::apiVersionIfUnspecified;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace RPC
 | 
			
		||||
 | 
			
		||||
template <unsigned minVer, unsigned maxVer, typename Fn, typename... Args>
 | 
			
		||||
 
 | 
			
		||||
@@ -103,6 +103,12 @@ public:
 | 
			
		||||
        return holds<Issue>() && get<Issue>().native();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    integral() const
 | 
			
		||||
    {
 | 
			
		||||
        return !holds<Issue>() || get<Issue>().native();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    friend constexpr bool
 | 
			
		||||
    operator==(Asset const& lhs, Asset const& rhs);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -346,6 +346,24 @@ vault(uint256 const& vaultKey)
 | 
			
		||||
    return {ltVAULT, vaultKey};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Keylet
 | 
			
		||||
loanbroker(AccountID const& owner, std::uint32_t seq) noexcept;
 | 
			
		||||
 | 
			
		||||
inline Keylet
 | 
			
		||||
loanbroker(uint256 const& key)
 | 
			
		||||
{
 | 
			
		||||
    return {ltLOAN_BROKER, key};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Keylet
 | 
			
		||||
loan(uint256 const& loanBrokerID, std::uint32_t loanSeq) noexcept;
 | 
			
		||||
 | 
			
		||||
inline Keylet
 | 
			
		||||
loan(uint256 const& key)
 | 
			
		||||
{
 | 
			
		||||
    return {ltLOAN, key};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Keylet
 | 
			
		||||
permissionedDomain(AccountID const& account, std::uint32_t seq) noexcept;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -205,6 +205,11 @@ enum LedgerSpecificFlags {
 | 
			
		||||
 | 
			
		||||
    // ltVAULT
 | 
			
		||||
    lsfVaultPrivate = 0x00010000,
 | 
			
		||||
 | 
			
		||||
    // ltLOAN
 | 
			
		||||
    lsfLoanDefault = 0x00010000,
 | 
			
		||||
    lsfLoanImpaired = 0x00020000,
 | 
			
		||||
    lsfLoanOverpayment = 0x00040000, // True, loan allows overpayments
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/ByteUtilities.h>
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/protocol/Units.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
@@ -84,6 +85,136 @@ std::size_t constexpr maxDeletableTokenOfferEntries = 500;
 | 
			
		||||
 */
 | 
			
		||||
std::uint16_t constexpr maxTransferFee = 50000;
 | 
			
		||||
 | 
			
		||||
/** There are 10,000 basis points (bips) in 100%.
 | 
			
		||||
 *
 | 
			
		||||
 * Basis points represent 0.01%.
 | 
			
		||||
 *
 | 
			
		||||
 * Given a value X, to find the amount for B bps,
 | 
			
		||||
 * use X * B / bipsPerUnity
 | 
			
		||||
 *
 | 
			
		||||
 * Example: If a loan broker has 999 XRP of debt, and must maintain 1,000 bps of
 | 
			
		||||
 * that debt as cover (10%), then the minimum cover amount is 999,000,000 drops
 | 
			
		||||
 * * 1000 / bipsPerUnity = 99,900,00 drops or 99.9 XRP.
 | 
			
		||||
 *
 | 
			
		||||
 * Given a percentage P, to find the number of bps that percentage represents,
 | 
			
		||||
 * use P * bipsPerUnity.
 | 
			
		||||
 *
 | 
			
		||||
 * Example: 50% is 0.50 * bipsPerUnity = 5,000 bps.
 | 
			
		||||
 */
 | 
			
		||||
Bips32 constexpr bipsPerUnity(100 * 100);
 | 
			
		||||
TenthBips32 constexpr tenthBipsPerUnity(bipsPerUnity.value() * 10);
 | 
			
		||||
 | 
			
		||||
constexpr Bips32
 | 
			
		||||
percentageToBips(std::uint32_t percentage)
 | 
			
		||||
{
 | 
			
		||||
    return Bips32(percentage * bipsPerUnity.value() / 100);
 | 
			
		||||
}
 | 
			
		||||
constexpr TenthBips32
 | 
			
		||||
percentageToTenthBips(std::uint32_t percentage)
 | 
			
		||||
{
 | 
			
		||||
    return TenthBips32(percentage * tenthBipsPerUnity.value() / 100);
 | 
			
		||||
}
 | 
			
		||||
template <typename T, class TBips>
 | 
			
		||||
constexpr T
 | 
			
		||||
bipsOfValue(T value, Bips<TBips> bips)
 | 
			
		||||
{
 | 
			
		||||
    return value * bips.value() / bipsPerUnity.value();
 | 
			
		||||
}
 | 
			
		||||
template <typename T, class TBips>
 | 
			
		||||
constexpr T
 | 
			
		||||
tenthBipsOfValue(T value, TenthBips<TBips> bips)
 | 
			
		||||
{
 | 
			
		||||
    return value * bips.value() / tenthBipsPerUnity.value();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** The maximum management fee rate allowed by a loan broker in 1/10 bips.
 | 
			
		||||
 | 
			
		||||
    Valid values are between 0 and 10% inclusive.
 | 
			
		||||
*/
 | 
			
		||||
TenthBips16 constexpr maxManagementFeeRate(
 | 
			
		||||
    unsafe_cast<std::uint16_t>(percentageToTenthBips(10).value()));
 | 
			
		||||
static_assert(maxManagementFeeRate == TenthBips16(std::uint16_t(10'000u)));
 | 
			
		||||
 | 
			
		||||
/** The maximum coverage rate required of a loan broker in 1/10 bips.
 | 
			
		||||
 | 
			
		||||
    Valid values are between 0 and 100% inclusive.
 | 
			
		||||
*/
 | 
			
		||||
TenthBips32 constexpr maxCoverRate = percentageToTenthBips(100);
 | 
			
		||||
static_assert(maxCoverRate == TenthBips32(100'000u));
 | 
			
		||||
 | 
			
		||||
/** The maximum overpayment fee on a loan in 1/10 bips.
 | 
			
		||||
*
 | 
			
		||||
    Valid values are between 0 and 100% inclusive.
 | 
			
		||||
*/
 | 
			
		||||
TenthBips32 constexpr maxOverpaymentFee = percentageToTenthBips(100);
 | 
			
		||||
static_assert(maxOverpaymentFee == TenthBips32(100'000u));
 | 
			
		||||
 | 
			
		||||
/** Annualized interest rate of the Loan in 1/10 bips.
 | 
			
		||||
 *
 | 
			
		||||
 * Valid values are between 0 and 100% inclusive.
 | 
			
		||||
 */
 | 
			
		||||
TenthBips32 constexpr maxInterestRate = percentageToTenthBips(100);
 | 
			
		||||
static_assert(maxInterestRate == TenthBips32(100'000u));
 | 
			
		||||
 | 
			
		||||
/** The maximum premium added to the interest rate for late payments on a loan
 | 
			
		||||
 * in 1/10 bips.
 | 
			
		||||
 *
 | 
			
		||||
 * Valid values are between 0 and 100% inclusive.
 | 
			
		||||
 */
 | 
			
		||||
TenthBips32 constexpr maxLateInterestRate = percentageToTenthBips(100);
 | 
			
		||||
static_assert(maxLateInterestRate == TenthBips32(100'000u));
 | 
			
		||||
 | 
			
		||||
/** The maximum close interest rate charged for repaying a loan early in 1/10
 | 
			
		||||
 * bips.
 | 
			
		||||
 *
 | 
			
		||||
 * Valid values are between 0 and 100% inclusive.
 | 
			
		||||
 */
 | 
			
		||||
TenthBips32 constexpr maxCloseInterestRate = percentageToTenthBips(100);
 | 
			
		||||
static_assert(maxCloseInterestRate == TenthBips32(100'000u));
 | 
			
		||||
 | 
			
		||||
/** The maximum overpayment interest rate charged on loan overpayments in 1/10
 | 
			
		||||
 * bips.
 | 
			
		||||
 *
 | 
			
		||||
 * Valid values are between 0 and 100% inclusive.
 | 
			
		||||
 */
 | 
			
		||||
TenthBips32 constexpr maxOverpaymentInterestRate = percentageToTenthBips(100);
 | 
			
		||||
static_assert(maxOverpaymentInterestRate == TenthBips32(100'000u));
 | 
			
		||||
 | 
			
		||||
/** LoanPay transaction cost will be one base fee per X combined payments
 | 
			
		||||
 *
 | 
			
		||||
 * The number of payments is estimated based on the Amount paid and the Loan's
 | 
			
		||||
 * Fixed Payment size. Overpayments (indicated with the tfLoanOverpayment flag)
 | 
			
		||||
 * count as one more payment.
 | 
			
		||||
 *
 | 
			
		||||
 * This number was chosen arbitrarily, but should not be changed once released
 | 
			
		||||
 * without an amendment
 | 
			
		||||
 */
 | 
			
		||||
static constexpr int loanPaymentsPerFeeIncrement = 5;
 | 
			
		||||
 | 
			
		||||
/** Maximum number of combined payments that a LoanPay transaction will process
 | 
			
		||||
 *
 | 
			
		||||
 * This limit is enforced during the loan payment process, and thus is not
 | 
			
		||||
 * estimated. If the limit is hit, no further payments or overpayments will be
 | 
			
		||||
 * processed, no matter how much of the transation Amount is left, but the
 | 
			
		||||
 * transaction will succeed with the payments that have been processed up to
 | 
			
		||||
 * that point.
 | 
			
		||||
 *
 | 
			
		||||
 * This limit is independent of loanPaymentsPerFeeIncrement, so a transaction
 | 
			
		||||
 * could potentially be charged for many more payments than actually get
 | 
			
		||||
 * processed. Users should take care not to submit a transaction paying more
 | 
			
		||||
 * than loanMaximumPaymentsPerTransaction * Loan.PeriodicPayment. Because
 | 
			
		||||
 * overpayments are charged as a payment, if submitting
 | 
			
		||||
 * loanMaximumPaymentsPerTransaction * Loan.PeriodicPayment, users should not
 | 
			
		||||
 * set the tfLoanOverpayment flag.
 | 
			
		||||
 *
 | 
			
		||||
 * Even though they're independent, loanMaximumPaymentsPerTransaction should be
 | 
			
		||||
 * a multiple of loanPaymentsPerFeeIncrement.
 | 
			
		||||
 *
 | 
			
		||||
 * This number was chosen arbitrarily, but should not be changed once released
 | 
			
		||||
 * without an amendment
 | 
			
		||||
 */
 | 
			
		||||
static constexpr int loanMaximumPaymentsPerTransaction = 100;
 | 
			
		||||
 | 
			
		||||
/** The maximum length of a URI inside an NFT */
 | 
			
		||||
std::size_t constexpr maxTokenURILength = 256;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -139,8 +139,8 @@ field_code(int id, int index)
 | 
			
		||||
    SFields are created at compile time.
 | 
			
		||||
 | 
			
		||||
    Each SField, once constructed, lives until program termination, and there
 | 
			
		||||
    is only one instance per fieldType/fieldValue pair which serves the entire
 | 
			
		||||
    application.
 | 
			
		||||
    is only one instance per fieldType/fieldValue pair which serves the
 | 
			
		||||
    entire application.
 | 
			
		||||
*/
 | 
			
		||||
class SField
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -66,15 +66,15 @@ public:
 | 
			
		||||
    static int const cMaxOffset = 80;
 | 
			
		||||
 | 
			
		||||
    // Maximum native value supported by the code
 | 
			
		||||
    static std::uint64_t const cMinValue = 1000000000000000ull;
 | 
			
		||||
    static std::uint64_t const cMaxValue = 9999999999999999ull;
 | 
			
		||||
    static std::uint64_t const cMaxNative = 9000000000000000000ull;
 | 
			
		||||
    static std::uint64_t const cMinValue = 1'000'000'000'000'000ull;
 | 
			
		||||
    static std::uint64_t const cMaxValue = 9'999'999'999'999'999ull;
 | 
			
		||||
    static std::uint64_t const cMaxNative = 9'000'000'000'000'000'000ull;
 | 
			
		||||
 | 
			
		||||
    // Max native value on network.
 | 
			
		||||
    static std::uint64_t const cMaxNativeN = 100000000000000000ull;
 | 
			
		||||
    static std::uint64_t const cIssuedCurrency = 0x8000000000000000ull;
 | 
			
		||||
    static std::uint64_t const cPositive = 0x4000000000000000ull;
 | 
			
		||||
    static std::uint64_t const cMPToken = 0x2000000000000000ull;
 | 
			
		||||
    static std::uint64_t const cMaxNativeN = 100'000'000'000'000'000ull;
 | 
			
		||||
    static std::uint64_t const cIssuedCurrency = 0x8'000'000'000'000'000ull;
 | 
			
		||||
    static std::uint64_t const cPositive = 0x4'000'000'000'000'000ull;
 | 
			
		||||
    static std::uint64_t const cMPToken = 0x2'000'000'000'000'000ull;
 | 
			
		||||
    static std::uint64_t const cValueMask = ~(cPositive | cMPToken);
 | 
			
		||||
 | 
			
		||||
    static std::uint64_t const uRateOne;
 | 
			
		||||
@@ -174,6 +174,9 @@ public:
 | 
			
		||||
    int
 | 
			
		||||
    exponent() const noexcept;
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    integral() const noexcept;
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    native() const noexcept;
 | 
			
		||||
 | 
			
		||||
@@ -454,6 +457,12 @@ STAmount::exponent() const noexcept
 | 
			
		||||
    return mOffset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool
 | 
			
		||||
STAmount::integral() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return mAsset.integral();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool
 | 
			
		||||
STAmount::native() const noexcept
 | 
			
		||||
{
 | 
			
		||||
@@ -695,6 +704,53 @@ divRoundStrict(
 | 
			
		||||
std::uint64_t
 | 
			
		||||
getRate(STAmount const& offerOut, STAmount const& offerIn);
 | 
			
		||||
 | 
			
		||||
/** Round an arbitrary precision Amount to the precision of an STAmount that has
 | 
			
		||||
 * a given exponent.
 | 
			
		||||
 *
 | 
			
		||||
 * This is used to ensure that calculations involving IOU amounts do not collect
 | 
			
		||||
 * dust beyond the precision of the reference value.
 | 
			
		||||
 *
 | 
			
		||||
 * @param value The value to be rounded
 | 
			
		||||
 * @param scale An exponent value to establish the precision limit of
 | 
			
		||||
 *     `value`. Should be larger than `value.exponent()`.
 | 
			
		||||
 * @param rounding Optional Number rounding mode
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
STAmount
 | 
			
		||||
roundToScale(
 | 
			
		||||
    STAmount value,
 | 
			
		||||
    std::int32_t scale,
 | 
			
		||||
    Number::rounding_mode rounding = Number::getround());
 | 
			
		||||
 | 
			
		||||
/** Round an arbitrary precision Number to the precision of a given Asset.
 | 
			
		||||
 *
 | 
			
		||||
 * This is used to ensure that calculations do not collect dust beyond the
 | 
			
		||||
 * precision of the reference value for IOUs, or fractional amounts for the
 | 
			
		||||
 * integral types XRP and MPT.
 | 
			
		||||
 *
 | 
			
		||||
 * @param asset The relevant asset
 | 
			
		||||
 * @param value The value to be rounded
 | 
			
		||||
 * @param scale Only relevant to IOU assets. An exponent value to establish the
 | 
			
		||||
 *      precision limit of `value`. Should be larger than `value.exponent()`.
 | 
			
		||||
 * @param rounding Optional Number rounding mode
 | 
			
		||||
 */
 | 
			
		||||
template <AssetType A>
 | 
			
		||||
Number
 | 
			
		||||
roundToAsset(
 | 
			
		||||
    A const& asset,
 | 
			
		||||
    Number const& value,
 | 
			
		||||
    std::int32_t scale,
 | 
			
		||||
    Number::rounding_mode rounding = Number::getround())
 | 
			
		||||
{
 | 
			
		||||
    NumberRoundModeGuard mg(rounding);
 | 
			
		||||
    STAmount const ret{asset, value};
 | 
			
		||||
    if (ret.integral())
 | 
			
		||||
        return ret;
 | 
			
		||||
    // Not that the ctor will round integral types (XRP, MPT) via canonicalize,
 | 
			
		||||
    // so no extra work is needed for those.
 | 
			
		||||
    return roundToScale(ret, scale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
inline bool
 | 
			
		||||
 
 | 
			
		||||
@@ -524,7 +524,26 @@ protected:
 | 
			
		||||
// Constraint += and -= ValueProxy operators
 | 
			
		||||
// to value types that support arithmetic operations
 | 
			
		||||
template <typename U>
 | 
			
		||||
concept IsArithmetic = std::is_arithmetic_v<U> || std::is_same_v<U, STAmount>;
 | 
			
		||||
concept IsArithmeticNumber = std::is_arithmetic_v<U> ||
 | 
			
		||||
    std::is_same_v<U, Number> || std::is_same_v<U, STAmount>;
 | 
			
		||||
template <
 | 
			
		||||
    typename U,
 | 
			
		||||
    typename Value = typename U::value_type,
 | 
			
		||||
    typename Unit = typename U::unit_type>
 | 
			
		||||
concept IsArithmeticValueUnit =
 | 
			
		||||
    std::is_same_v<U, unit::ValueUnit<Unit, Value>> &&
 | 
			
		||||
    IsArithmeticNumber<Value> && std::is_class_v<Unit>;
 | 
			
		||||
template <typename U, typename Value = typename U::value_type>
 | 
			
		||||
concept IsArithmeticST = !IsArithmeticValueUnit<U> && IsArithmeticNumber<Value>;
 | 
			
		||||
template <typename U>
 | 
			
		||||
concept IsArithmetic =
 | 
			
		||||
    IsArithmeticNumber<U> || IsArithmeticST<U> || IsArithmeticValueUnit<U>;
 | 
			
		||||
 | 
			
		||||
template <class T, class U>
 | 
			
		||||
concept Addable = requires(T t, U u) { t = t + u; };
 | 
			
		||||
template <typename T, typename U>
 | 
			
		||||
concept IsArithmeticCompatible =
 | 
			
		||||
    IsArithmetic<typename T::value_type> && Addable<typename T::value_type, U>;
 | 
			
		||||
 | 
			
		||||
template <class T>
 | 
			
		||||
class STObject::ValueProxy : public Proxy<T>
 | 
			
		||||
@@ -544,10 +563,12 @@ public:
 | 
			
		||||
    // Convenience operators for value types supporting
 | 
			
		||||
    // arithmetic operations
 | 
			
		||||
    template <IsArithmetic U>
 | 
			
		||||
        requires IsArithmeticCompatible<T, U>
 | 
			
		||||
    ValueProxy&
 | 
			
		||||
    operator+=(U const& u);
 | 
			
		||||
 | 
			
		||||
    template <IsArithmetic U>
 | 
			
		||||
        requires IsArithmeticCompatible<T, U>
 | 
			
		||||
    ValueProxy&
 | 
			
		||||
    operator-=(U const& u);
 | 
			
		||||
 | 
			
		||||
@@ -783,6 +804,7 @@ STObject::ValueProxy<T>::operator=(U&& u)
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
template <IsArithmetic U>
 | 
			
		||||
    requires IsArithmeticCompatible<T, U>
 | 
			
		||||
STObject::ValueProxy<T>&
 | 
			
		||||
STObject::ValueProxy<T>::operator+=(U const& u)
 | 
			
		||||
{
 | 
			
		||||
@@ -792,6 +814,7 @@ STObject::ValueProxy<T>::operator+=(U const& u)
 | 
			
		||||
 | 
			
		||||
template <class T>
 | 
			
		||||
template <IsArithmetic U>
 | 
			
		||||
    requires IsArithmeticCompatible<T, U>
 | 
			
		||||
STObject::ValueProxy<T>&
 | 
			
		||||
STObject::ValueProxy<T>::operator-=(U const& u)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -73,14 +73,8 @@ static constexpr std::uint32_t XRP_LEDGER_EARLIEST_SEQ{32570u};
 | 
			
		||||
 * used in asserts and tests. */
 | 
			
		||||
static constexpr std::uint32_t XRP_LEDGER_EARLIEST_FEES{562177u};
 | 
			
		||||
 | 
			
		||||
/** The minimum amount of support an amendment should have.
 | 
			
		||||
 | 
			
		||||
    @note This value is used by legacy code and will become obsolete
 | 
			
		||||
          once the fixAmendmentMajorityCalc amendment activates.
 | 
			
		||||
*/
 | 
			
		||||
constexpr std::ratio<204, 256> preFixAmendmentMajorityCalcThreshold;
 | 
			
		||||
 | 
			
		||||
constexpr std::ratio<80, 100> postFixAmendmentMajorityCalcThreshold;
 | 
			
		||||
/** The minimum amount of support an amendment should have. */
 | 
			
		||||
constexpr std::ratio<80, 100> amendmentMajorityCalcThreshold;
 | 
			
		||||
 | 
			
		||||
/** The minimum amount of time an amendment must hold a majority */
 | 
			
		||||
constexpr std::chrono::seconds const defaultAmendmentMajorityTime = weeks{2};
 | 
			
		||||
 
 | 
			
		||||
@@ -225,8 +225,9 @@ enum TERcodes : TERUnderlyingType {
 | 
			
		||||
    terQUEUED,       // Transaction is being held in TxQ until fee drops
 | 
			
		||||
    terPRE_TICKET,   // Ticket is not yet in ledger but might be on its way
 | 
			
		||||
    terNO_AMM,       // AMM doesn't exist for the asset pair
 | 
			
		||||
    terADDRESS_COLLISION,  // Failed to allocate AccountID when trying to
 | 
			
		||||
                           // create a pseudo-account
 | 
			
		||||
    terADDRESS_COLLISION,       // Failed to allocate AccountID when trying to
 | 
			
		||||
                                // create a pseudo-account
 | 
			
		||||
    terNO_DELEGATE_PERMISSION,  // Delegate does not have permission
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
@@ -361,6 +362,9 @@ enum TECcodes : TERUnderlyingType {
 | 
			
		||||
    tecLIMIT_EXCEEDED = 195,
 | 
			
		||||
    tecPSEUDO_ACCOUNT = 196,
 | 
			
		||||
    tecPRECISION_LOSS = 197,
 | 
			
		||||
    // DEPRECATED: This error code tecNO_DELEGATE_PERMISSION is reserved for
 | 
			
		||||
    // backward compatibility with historical data on non-prod networks, can be
 | 
			
		||||
    // reclaimed after those networks reset.
 | 
			
		||||
    tecNO_DELEGATE_PERMISSION = 198,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -285,6 +285,25 @@ constexpr std::uint32_t tfIndependent                  = 0x00080000;
 | 
			
		||||
constexpr std::uint32_t const tfBatchMask =
 | 
			
		||||
    ~(tfUniversal | tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent) | tfInnerBatchTxn;
 | 
			
		||||
 | 
			
		||||
// LoanSet and LoanPay flags:
 | 
			
		||||
// LoanSet: True, indicates the loan supports overpayments
 | 
			
		||||
// LoanPay: True, indicates any excess in this payment can be used
 | 
			
		||||
// as an overpayment. False, no overpayments will be taken.
 | 
			
		||||
constexpr std::uint32_t const tfLoanOverpayment = 0x00010000;
 | 
			
		||||
// LoanPay exclusive flags:
 | 
			
		||||
// tfLoanFullPayment: True, indicates that the payment is
 | 
			
		||||
constexpr std::uint32_t const tfLoanFullPayment = 0x00020000;
 | 
			
		||||
constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal |
 | 
			
		||||
    tfLoanOverpayment);
 | 
			
		||||
constexpr std::uint32_t const tfLoanPayMask = ~(tfUniversal |
 | 
			
		||||
    tfLoanOverpayment | tfLoanFullPayment);
 | 
			
		||||
 | 
			
		||||
// LoanManage flags:
 | 
			
		||||
constexpr std::uint32_t const tfLoanDefault = 0x00010000;
 | 
			
		||||
constexpr std::uint32_t const tfLoanImpair = 0x00020000;
 | 
			
		||||
constexpr std::uint32_t const tfLoanUnimpair = 0x00040000;
 | 
			
		||||
constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair);
 | 
			
		||||
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
 
 | 
			
		||||
@@ -33,51 +33,35 @@ namespace ripple {
 | 
			
		||||
 | 
			
		||||
class TxMeta
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    struct CtorHelper
 | 
			
		||||
    {
 | 
			
		||||
        explicit CtorHelper() = default;
 | 
			
		||||
    };
 | 
			
		||||
    template <class T>
 | 
			
		||||
    TxMeta(
 | 
			
		||||
        uint256 const& txID,
 | 
			
		||||
        std::uint32_t ledger,
 | 
			
		||||
        T const& data,
 | 
			
		||||
        CtorHelper);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    TxMeta(
 | 
			
		||||
        uint256 const& transactionID,
 | 
			
		||||
        std::uint32_t ledger,
 | 
			
		||||
        std::optional<uint256> parentBatchId = std::nullopt);
 | 
			
		||||
    TxMeta(uint256 const& transactionID, std::uint32_t ledger);
 | 
			
		||||
    TxMeta(uint256 const& txID, std::uint32_t ledger, Blob const&);
 | 
			
		||||
    TxMeta(uint256 const& txID, std::uint32_t ledger, std::string const&);
 | 
			
		||||
    TxMeta(uint256 const& txID, std::uint32_t ledger, STObject const&);
 | 
			
		||||
 | 
			
		||||
    uint256 const&
 | 
			
		||||
    getTxID() const
 | 
			
		||||
    {
 | 
			
		||||
        return mTransactionID;
 | 
			
		||||
        return transactionID_;
 | 
			
		||||
    }
 | 
			
		||||
    std::uint32_t
 | 
			
		||||
    getLgrSeq() const
 | 
			
		||||
    {
 | 
			
		||||
        return mLedger;
 | 
			
		||||
        return ledgerSeq_;
 | 
			
		||||
    }
 | 
			
		||||
    int
 | 
			
		||||
    getResult() const
 | 
			
		||||
    {
 | 
			
		||||
        return mResult;
 | 
			
		||||
        return result_;
 | 
			
		||||
    }
 | 
			
		||||
    TER
 | 
			
		||||
    getResultTER() const
 | 
			
		||||
    {
 | 
			
		||||
        return TER::fromInt(mResult);
 | 
			
		||||
        return TER::fromInt(result_);
 | 
			
		||||
    }
 | 
			
		||||
    std::uint32_t
 | 
			
		||||
    getIndex() const
 | 
			
		||||
    {
 | 
			
		||||
        return mIndex;
 | 
			
		||||
        return index_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
@@ -104,66 +88,52 @@ public:
 | 
			
		||||
    STArray&
 | 
			
		||||
    getNodes()
 | 
			
		||||
    {
 | 
			
		||||
        return (mNodes);
 | 
			
		||||
        return nodes_;
 | 
			
		||||
    }
 | 
			
		||||
    STArray const&
 | 
			
		||||
    getNodes() const
 | 
			
		||||
    {
 | 
			
		||||
        return (mNodes);
 | 
			
		||||
        return nodes_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    setDeliveredAmount(STAmount const& delivered)
 | 
			
		||||
    setAdditionalFields(STObject const& obj)
 | 
			
		||||
    {
 | 
			
		||||
        mDelivered = delivered;
 | 
			
		||||
        if (obj.isFieldPresent(sfDeliveredAmount))
 | 
			
		||||
            deliveredAmount_ = obj.getFieldAmount(sfDeliveredAmount);
 | 
			
		||||
 | 
			
		||||
        if (obj.isFieldPresent(sfParentBatchID))
 | 
			
		||||
            parentBatchID_ = obj.getFieldH256(sfParentBatchID);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    STAmount
 | 
			
		||||
    std::optional<STAmount> const&
 | 
			
		||||
    getDeliveredAmount() const
 | 
			
		||||
    {
 | 
			
		||||
        XRPL_ASSERT(
 | 
			
		||||
            hasDeliveredAmount(),
 | 
			
		||||
            "ripple::TxMeta::getDeliveredAmount : non-null delivered amount");
 | 
			
		||||
        return *mDelivered;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    hasDeliveredAmount() const
 | 
			
		||||
    {
 | 
			
		||||
        return static_cast<bool>(mDelivered);
 | 
			
		||||
        return deliveredAmount_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    setParentBatchId(uint256 const& parentBatchId)
 | 
			
		||||
    setDeliveredAmount(std::optional<STAmount> const& amount)
 | 
			
		||||
    {
 | 
			
		||||
        mParentBatchId = parentBatchId;
 | 
			
		||||
        deliveredAmount_ = amount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint256
 | 
			
		||||
    getParentBatchId() const
 | 
			
		||||
    void
 | 
			
		||||
    setParentBatchID(std::optional<uint256> const& id)
 | 
			
		||||
    {
 | 
			
		||||
        XRPL_ASSERT(
 | 
			
		||||
            hasParentBatchId(),
 | 
			
		||||
            "ripple::TxMeta::getParentBatchId : non-null batch id");
 | 
			
		||||
        return *mParentBatchId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
    hasParentBatchId() const
 | 
			
		||||
    {
 | 
			
		||||
        return static_cast<bool>(mParentBatchId);
 | 
			
		||||
        parentBatchID_ = id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    uint256 mTransactionID;
 | 
			
		||||
    std::uint32_t mLedger;
 | 
			
		||||
    std::uint32_t mIndex;
 | 
			
		||||
    int mResult;
 | 
			
		||||
    uint256 transactionID_;
 | 
			
		||||
    std::uint32_t ledgerSeq_;
 | 
			
		||||
    std::uint32_t index_;
 | 
			
		||||
    int result_;
 | 
			
		||||
 | 
			
		||||
    std::optional<STAmount> mDelivered;
 | 
			
		||||
    std::optional<uint256> mParentBatchId;
 | 
			
		||||
    std::optional<STAmount> deliveredAmount_;
 | 
			
		||||
    std::optional<uint256> parentBatchID_;
 | 
			
		||||
 | 
			
		||||
    STArray mNodes;
 | 
			
		||||
    STArray nodes_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
 
 | 
			
		||||
@@ -27,14 +27,17 @@
 | 
			
		||||
#error "undefined macro: XRPL_RETIRE"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
 | 
			
		||||
// Add new amendments to the top of this list.
 | 
			
		||||
// Keep it sorted in reverse chronological order.
 | 
			
		||||
 | 
			
		||||
XRPL_FEATURE(LendingProtocol,            Supported::no,  VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(PermissionDelegationV1_1,   Supported::no,  VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (DirectoryLimit,             Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (IncludeKeyletFields,        Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(DynamicMPT,                 Supported::no,  VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (TokenEscrowV1,              Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (DelegateV1_1,               Supported::no,  VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (PriceOracleOrder,           Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (MPTDeliveredAmount,         Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (AMMClawbackRounding,        Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
@@ -44,7 +47,6 @@ XRPL_FIX    (AMMv1_3,                    Supported::yes, VoteBehavior::DefaultNo
 | 
			
		||||
XRPL_FEATURE(PermissionedDEX,            Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(Batch,                      Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(SingleAssetVault,           Supported::no,  VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(PermissionDelegation,       Supported::no,  VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (PayChanCancelAfter,         Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
// Check flags in Credential transactions
 | 
			
		||||
XRPL_FIX    (InvalidTxFlags,             Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
@@ -78,27 +80,20 @@ XRPL_FIX    (DisallowIncomingV1,         Supported::yes, VoteBehavior::DefaultNo
 | 
			
		||||
XRPL_FEATURE(XChainBridge,               Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(AMM,                        Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(Clawback,                   Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (ReducedOffersV1,            Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (NFTokenRemint,              Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (NonFungibleTokensV1_2,      Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (UniversalNumber,            Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(XRPFees,                    Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(DisallowIncoming,           Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(ImmediateOfferKilled,       Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FIX    (RemoveNFTokenAutoTrustLine, Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FIX    (TrustLinesToSelf,           Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(NonFungibleTokensV1_1,      Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(ExpandedSignerList,         Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(CheckCashMakesTrustLine,    Supported::yes, VoteBehavior::DefaultNo)
 | 
			
		||||
XRPL_FEATURE(FlowSortStrands,            Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(TicketBatch,                Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(NegativeUNL,                Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FIX    (AmendmentMajorityCalc,      Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(HardenedValidations,        Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(RequireFullyCanonicalSig,   Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(DeletableAccounts,          Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FIX    (PayChanRecipientOwnerDir,   Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FIX    (MasterKeyAsRegularKey,      Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(MultiSignReserve,           Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(DepositPreauth,             Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
XRPL_FEATURE(Checks,                     Supported::yes, VoteBehavior::DefaultYes)
 | 
			
		||||
@@ -116,9 +111,6 @@ XRPL_FEATURE(Flow,                       Supported::yes, VoteBehavior::DefaultYe
 | 
			
		||||
//
 | 
			
		||||
// If a feature remains obsolete for long enough that no clients are able
 | 
			
		||||
// to vote for it, the feature can be removed (entirely?) from the code.
 | 
			
		||||
XRPL_FIX    (NFTokenNegOffer,       Supported::yes, VoteBehavior::Obsolete)
 | 
			
		||||
XRPL_FIX    (NFTokenDirV1,          Supported::yes, VoteBehavior::Obsolete)
 | 
			
		||||
XRPL_FEATURE(NonFungibleTokensV1,   Supported::yes, VoteBehavior::Obsolete)
 | 
			
		||||
XRPL_FEATURE(CryptoConditionsSuite, Supported::yes, VoteBehavior::Obsolete)
 | 
			
		||||
 | 
			
		||||
// The following amendments have been active for at least two years. Their
 | 
			
		||||
@@ -140,8 +132,13 @@ XRPL_RETIRE(fix1571)
 | 
			
		||||
XRPL_RETIRE(fix1578)
 | 
			
		||||
XRPL_RETIRE(fix1623)
 | 
			
		||||
XRPL_RETIRE(fix1781)
 | 
			
		||||
XRPL_RETIRE(fixAmendmentMajorityCalc)
 | 
			
		||||
XRPL_RETIRE(fixCheckThreading)
 | 
			
		||||
XRPL_RETIRE(fixNonFungibleTokensV1_2)
 | 
			
		||||
XRPL_RETIRE(fixNFTokenRemint)
 | 
			
		||||
XRPL_RETIRE(fixMasterKeyAsRegularKey)
 | 
			
		||||
XRPL_RETIRE(fixQualityUpperBound)
 | 
			
		||||
XRPL_RETIRE(fixReducedOffersV1)
 | 
			
		||||
XRPL_RETIRE(fixRmSmallIncreasedQOffers)
 | 
			
		||||
XRPL_RETIRE(fixSTAmountCanonicalize)
 | 
			
		||||
XRPL_RETIRE(fixTakerDryOfferRemoval)
 | 
			
		||||
@@ -150,8 +147,12 @@ XRPL_RETIRE(Escrow)
 | 
			
		||||
XRPL_RETIRE(EnforceInvariants)
 | 
			
		||||
XRPL_RETIRE(FeeEscalation)
 | 
			
		||||
XRPL_RETIRE(FlowCross)
 | 
			
		||||
XRPL_RETIRE(ImmediateOfferKilled)
 | 
			
		||||
XRPL_RETIRE(MultiSign)
 | 
			
		||||
XRPL_RETIRE(NonFungibleTokensV1_1)
 | 
			
		||||
XRPL_RETIRE(PayChan)
 | 
			
		||||
XRPL_RETIRE(SortedDirectories)
 | 
			
		||||
XRPL_RETIRE(TickSize)
 | 
			
		||||
XRPL_RETIRE(TrustSetAuth)
 | 
			
		||||
 | 
			
		||||
// clang-format on
 | 
			
		||||
 
 | 
			
		||||
@@ -168,6 +168,7 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({
 | 
			
		||||
    {sfFirstNFTokenSequence, soeOPTIONAL},
 | 
			
		||||
    {sfAMMID,                soeOPTIONAL}, // pseudo-account designator
 | 
			
		||||
    {sfVaultID,              soeOPTIONAL}, // pseudo-account designator
 | 
			
		||||
    {sfLoanBrokerID,         soeOPTIONAL}, // pseudo-account designator
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** A ledger object which contains a list of object identifiers.
 | 
			
		||||
@@ -509,5 +510,117 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
 | 
			
		||||
    // no PermissionedDomainID ever (use MPTIssuance.sfDomainID)
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** Reserve 0x0084-0x0087 for future Vault-related objects. */
 | 
			
		||||
 | 
			
		||||
/** A ledger object representing a loan broker
 | 
			
		||||
 | 
			
		||||
    \sa keylet::loanbroker
 | 
			
		||||
 */
 | 
			
		||||
LEDGER_ENTRY(ltLOAN_BROKER, 0x0088, LoanBroker, loan_broker, ({
 | 
			
		||||
    {sfPreviousTxnID,        soeREQUIRED},
 | 
			
		||||
    {sfPreviousTxnLgrSeq,    soeREQUIRED},
 | 
			
		||||
    {sfSequence,             soeREQUIRED},
 | 
			
		||||
    {sfOwnerNode,            soeREQUIRED},
 | 
			
		||||
    {sfVaultNode,            soeREQUIRED},
 | 
			
		||||
    {sfVaultID,              soeREQUIRED},
 | 
			
		||||
    {sfAccount,              soeREQUIRED},
 | 
			
		||||
    {sfOwner,                soeREQUIRED},
 | 
			
		||||
    {sfLoanSequence,         soeREQUIRED},
 | 
			
		||||
    {sfData,                 soeDEFAULT},
 | 
			
		||||
    {sfManagementFeeRate,    soeDEFAULT},
 | 
			
		||||
    {sfOwnerCount,           soeDEFAULT},
 | 
			
		||||
    {sfDebtTotal,            soeDEFAULT},
 | 
			
		||||
    {sfDebtMaximum,          soeDEFAULT},
 | 
			
		||||
    {sfCoverAvailable,       soeDEFAULT},
 | 
			
		||||
    {sfCoverRateMinimum,     soeDEFAULT},
 | 
			
		||||
    {sfCoverRateLiquidation, soeDEFAULT},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** A ledger object representing a loan between a Borrower and a Loan Broker
 | 
			
		||||
 | 
			
		||||
    \sa keylet::loan
 | 
			
		||||
 */
 | 
			
		||||
LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
 | 
			
		||||
    {sfPreviousTxnID,        soeREQUIRED},
 | 
			
		||||
    {sfPreviousTxnLgrSeq,    soeREQUIRED},
 | 
			
		||||
    {sfOwnerNode,            soeREQUIRED},
 | 
			
		||||
    {sfLoanBrokerNode,       soeREQUIRED},
 | 
			
		||||
    {sfLoanBrokerID,         soeREQUIRED},
 | 
			
		||||
    {sfLoanSequence,         soeREQUIRED},
 | 
			
		||||
    {sfBorrower,             soeREQUIRED},
 | 
			
		||||
    {sfLoanOriginationFee,   soeDEFAULT},
 | 
			
		||||
    {sfLoanServiceFee,       soeDEFAULT},
 | 
			
		||||
    {sfLatePaymentFee,       soeDEFAULT},
 | 
			
		||||
    {sfClosePaymentFee,      soeDEFAULT},
 | 
			
		||||
    {sfOverpaymentFee,       soeDEFAULT},
 | 
			
		||||
    {sfInterestRate,         soeDEFAULT},
 | 
			
		||||
    {sfLateInterestRate,     soeDEFAULT},
 | 
			
		||||
    {sfCloseInterestRate,    soeDEFAULT},
 | 
			
		||||
    {sfOverpaymentInterestRate, soeDEFAULT},
 | 
			
		||||
    {sfStartDate,            soeREQUIRED},
 | 
			
		||||
    {sfPaymentInterval,      soeREQUIRED},
 | 
			
		||||
    {sfGracePeriod,          soeREQUIRED},
 | 
			
		||||
    {sfPreviousPaymentDate,  soeDEFAULT},
 | 
			
		||||
    {sfNextPaymentDueDate,   soeOPTIONAL},
 | 
			
		||||
    // The loan object tracks these values:
 | 
			
		||||
    //
 | 
			
		||||
    // - PaymentRemaining: The number of payments left in the loan. When it
 | 
			
		||||
    //   reaches 0, the loan is paid off, and all other relevant values
 | 
			
		||||
    //   must also be 0.
 | 
			
		||||
    //
 | 
			
		||||
    // - PeriodicPayment: The fixed, unrounded amount to be paid each
 | 
			
		||||
    //   interval. Stored with as much precision as possible.
 | 
			
		||||
    //   Payment transactions must round this value *UP*.
 | 
			
		||||
    //
 | 
			
		||||
    // - TotalValueOutstanding: The rounded total amount owed by the
 | 
			
		||||
    //   borrower to the lender / vault.
 | 
			
		||||
    //
 | 
			
		||||
    // - PrincipalOutstanding: The rounded portion of the
 | 
			
		||||
    //   TotalValueOutstanding that is from the principal borrowed.
 | 
			
		||||
    //
 | 
			
		||||
    // - ManagementFeeOutstanding: The rounded portion of the
 | 
			
		||||
    //   TotalValueOutstanding that represents management fees
 | 
			
		||||
    //   specifically owed to the broker based on the initial
 | 
			
		||||
    //   loan parameters.
 | 
			
		||||
    //
 | 
			
		||||
    // There are additional values that can be computed from these:
 | 
			
		||||
    //
 | 
			
		||||
    // - InterestOutstanding = TotalValueOutstanding - PrincipalOutstanding
 | 
			
		||||
    //   The total amount of interest still pending on the loan,
 | 
			
		||||
    //   independent of management fees.
 | 
			
		||||
    //
 | 
			
		||||
    // - InterestOwedToVault = InterestOutstanding - ManagementFeeOutstanding
 | 
			
		||||
    //   The amount of the total interest that is owed to the vault, and
 | 
			
		||||
    //   will be sent to as part of a payment.
 | 
			
		||||
    //
 | 
			
		||||
    // - TrueTotalLoanValue = PaymentRemaining * PeriodicPayment
 | 
			
		||||
    //   The unrounded true total value of the loan.
 | 
			
		||||
    //
 | 
			
		||||
    // - TrueTotalPrincialOutstanding can be computed using the algorithm
 | 
			
		||||
    //   in the ripple::detail::loanPrincipalFromPeriodicPayment function.
 | 
			
		||||
    //
 | 
			
		||||
    // - TrueTotalInterestOutstanding = TrueTotalLoanValue -
 | 
			
		||||
    //      TrueTotalPrincipalOutstanding
 | 
			
		||||
    //   The unrounded true total interest remaining.
 | 
			
		||||
    //
 | 
			
		||||
    // - TrueTotalManagementFeeOutstanding = TrueTotalInterestOutstanding *
 | 
			
		||||
    //      LoanBroker.ManagementFeeRate
 | 
			
		||||
    //   The unrounded true total fee still owed to the broker.
 | 
			
		||||
    //
 | 
			
		||||
    // Note the the "True" values may differ significantly from the tracked
 | 
			
		||||
    // rounded values.
 | 
			
		||||
    {sfPaymentRemaining,         soeDEFAULT},
 | 
			
		||||
    {sfPeriodicPayment,          soeREQUIRED},
 | 
			
		||||
    {sfPrincipalOutstanding,     soeDEFAULT},
 | 
			
		||||
    {sfTotalValueOutstanding,    soeDEFAULT},
 | 
			
		||||
    {sfManagementFeeOutstanding, soeDEFAULT},
 | 
			
		||||
    // Based on the computed total value at creation, used for
 | 
			
		||||
    // rounding calculated values so they are all on a
 | 
			
		||||
    // consistent scale - that is, they all have the same
 | 
			
		||||
    // number of digits after the decimal point (excluding
 | 
			
		||||
    // trailing zeros).
 | 
			
		||||
    {sfLoanScale,                soeDEFAULT},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
#undef EXPAND
 | 
			
		||||
#undef LEDGER_ENTRY_DUPLICATE
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,8 @@
 | 
			
		||||
#error "undefined macro: TYPED_SFIELD"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
 | 
			
		||||
// untyped
 | 
			
		||||
UNTYPED_SFIELD(sfLedgerEntry,            LEDGERENTRY, 257)
 | 
			
		||||
UNTYPED_SFIELD(sfTransaction,            TRANSACTION, 257)
 | 
			
		||||
@@ -59,6 +61,7 @@ TYPED_SFIELD(sfHookEmitCount,            UINT16,    18)
 | 
			
		||||
TYPED_SFIELD(sfHookExecutionIndex,       UINT16,    19)
 | 
			
		||||
TYPED_SFIELD(sfHookApiVersion,           UINT16,    20)
 | 
			
		||||
TYPED_SFIELD(sfLedgerFixType,            UINT16,    21)
 | 
			
		||||
TYPED_SFIELD(sfManagementFeeRate,        UINT16,    22) // 1/10 basis points (bips)
 | 
			
		||||
 | 
			
		||||
// 32-bit integers (common)
 | 
			
		||||
TYPED_SFIELD(sfNetworkID,                UINT32,     1)
 | 
			
		||||
@@ -115,6 +118,21 @@ TYPED_SFIELD(sfFirstNFTokenSequence,     UINT32,    50)
 | 
			
		||||
TYPED_SFIELD(sfOracleDocumentID,         UINT32,    51)
 | 
			
		||||
TYPED_SFIELD(sfPermissionValue,          UINT32,    52)
 | 
			
		||||
TYPED_SFIELD(sfMutableFlags,             UINT32,    53)
 | 
			
		||||
TYPED_SFIELD(sfStartDate,                UINT32,    54)
 | 
			
		||||
TYPED_SFIELD(sfPaymentInterval,          UINT32,    55)
 | 
			
		||||
TYPED_SFIELD(sfGracePeriod,              UINT32,    56)
 | 
			
		||||
TYPED_SFIELD(sfPreviousPaymentDate,      UINT32,    57)
 | 
			
		||||
TYPED_SFIELD(sfNextPaymentDueDate,       UINT32,    58)
 | 
			
		||||
TYPED_SFIELD(sfPaymentRemaining,         UINT32,    59)
 | 
			
		||||
TYPED_SFIELD(sfPaymentTotal,             UINT32,    60)
 | 
			
		||||
TYPED_SFIELD(sfLoanSequence,             UINT32,    61)
 | 
			
		||||
TYPED_SFIELD(sfCoverRateMinimum,         UINT32,    62) // 1/10 basis points (bips)
 | 
			
		||||
TYPED_SFIELD(sfCoverRateLiquidation,     UINT32,    63) // 1/10 basis points (bips)
 | 
			
		||||
TYPED_SFIELD(sfOverpaymentFee,           UINT32,    64) // 1/10 basis points (bips)
 | 
			
		||||
TYPED_SFIELD(sfInterestRate,             UINT32,    65) // 1/10 basis points (bips)
 | 
			
		||||
TYPED_SFIELD(sfLateInterestRate,         UINT32,    66) // 1/10 basis points (bips)
 | 
			
		||||
TYPED_SFIELD(sfCloseInterestRate,        UINT32,    67) // 1/10 basis points (bips)
 | 
			
		||||
TYPED_SFIELD(sfOverpaymentInterestRate,  UINT32,    68) // 1/10 basis points (bips)
 | 
			
		||||
 | 
			
		||||
// 64-bit integers (common)
 | 
			
		||||
TYPED_SFIELD(sfIndexNext,                UINT64,     1)
 | 
			
		||||
@@ -146,6 +164,8 @@ TYPED_SFIELD(sfMPTAmount,                UINT64,    26, SField::sMD_BaseTen|SFie
 | 
			
		||||
TYPED_SFIELD(sfIssuerNode,               UINT64,    27)
 | 
			
		||||
TYPED_SFIELD(sfSubjectNode,              UINT64,    28)
 | 
			
		||||
TYPED_SFIELD(sfLockedAmount,             UINT64,    29, SField::sMD_BaseTen|SField::sMD_Default)
 | 
			
		||||
TYPED_SFIELD(sfVaultNode,                UINT64,    30)
 | 
			
		||||
TYPED_SFIELD(sfLoanBrokerNode,           UINT64,    31)
 | 
			
		||||
 | 
			
		||||
// 128-bit
 | 
			
		||||
TYPED_SFIELD(sfEmailHash,                UINT128,    1)
 | 
			
		||||
@@ -200,6 +220,9 @@ TYPED_SFIELD(sfDomainID,                 UINT256,   34)
 | 
			
		||||
TYPED_SFIELD(sfVaultID,                  UINT256,   35,
 | 
			
		||||
    SField::sMD_PseudoAccount | SField::sMD_Default)
 | 
			
		||||
TYPED_SFIELD(sfParentBatchID,            UINT256,   36)
 | 
			
		||||
TYPED_SFIELD(sfLoanBrokerID,             UINT256,   37,
 | 
			
		||||
    SField::sMD_PseudoAccount | SField::sMD_Default)
 | 
			
		||||
TYPED_SFIELD(sfLoanID,                   UINT256,   38)
 | 
			
		||||
 | 
			
		||||
// number (common)
 | 
			
		||||
TYPED_SFIELD(sfNumber,                   NUMBER,     1)
 | 
			
		||||
@@ -207,12 +230,21 @@ TYPED_SFIELD(sfAssetsAvailable,          NUMBER,     2)
 | 
			
		||||
TYPED_SFIELD(sfAssetsMaximum,            NUMBER,     3)
 | 
			
		||||
TYPED_SFIELD(sfAssetsTotal,              NUMBER,     4)
 | 
			
		||||
TYPED_SFIELD(sfLossUnrealized,           NUMBER,     5)
 | 
			
		||||
TYPED_SFIELD(sfDebtTotal,                NUMBER,     6)
 | 
			
		||||
TYPED_SFIELD(sfDebtMaximum,              NUMBER,     7)
 | 
			
		||||
TYPED_SFIELD(sfCoverAvailable,           NUMBER,     8)
 | 
			
		||||
TYPED_SFIELD(sfLoanOriginationFee,       NUMBER,     9)
 | 
			
		||||
TYPED_SFIELD(sfLoanServiceFee,           NUMBER,     10)
 | 
			
		||||
TYPED_SFIELD(sfLatePaymentFee,           NUMBER,     11)
 | 
			
		||||
TYPED_SFIELD(sfClosePaymentFee,          NUMBER,     12)
 | 
			
		||||
TYPED_SFIELD(sfPrincipalOutstanding,     NUMBER,     13)
 | 
			
		||||
TYPED_SFIELD(sfPrincipalRequested,       NUMBER,     14)
 | 
			
		||||
TYPED_SFIELD(sfTotalValueOutstanding,    NUMBER,     15)
 | 
			
		||||
TYPED_SFIELD(sfPeriodicPayment,          NUMBER,     16)
 | 
			
		||||
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER,     17)
 | 
			
		||||
 | 
			
		||||
// int32
 | 
			
		||||
// NOTE: Do not use `sfDummyInt32`. It's so far the only use of INT32
 | 
			
		||||
// in this file and has been defined here for test only.
 | 
			
		||||
// TODO: Replace `sfDummyInt32` with actually useful field.
 | 
			
		||||
TYPED_SFIELD(sfDummyInt32,               INT32,      1) // for tests only
 | 
			
		||||
TYPED_SFIELD(sfLoanScale,                INT32,      1)
 | 
			
		||||
 | 
			
		||||
// currency amount (common)
 | 
			
		||||
TYPED_SFIELD(sfAmount,                   AMOUNT,     1)
 | 
			
		||||
@@ -308,6 +340,8 @@ TYPED_SFIELD(sfAttestationRewardAccount, ACCOUNT,   21)
 | 
			
		||||
TYPED_SFIELD(sfLockingChainDoor,         ACCOUNT,   22)
 | 
			
		||||
TYPED_SFIELD(sfIssuingChainDoor,         ACCOUNT,   23)
 | 
			
		||||
TYPED_SFIELD(sfSubject,                  ACCOUNT,   24)
 | 
			
		||||
TYPED_SFIELD(sfBorrower,                 ACCOUNT,   25)
 | 
			
		||||
TYPED_SFIELD(sfCounterparty,             ACCOUNT,   26)
 | 
			
		||||
 | 
			
		||||
// vector of 256-bit
 | 
			
		||||
TYPED_SFIELD(sfIndexes,                  VECTOR256,  1, SField::sMD_Never)
 | 
			
		||||
@@ -371,6 +405,7 @@ UNTYPED_SFIELD(sfCredential,             OBJECT,    33)
 | 
			
		||||
UNTYPED_SFIELD(sfRawTransaction,         OBJECT,    34)
 | 
			
		||||
UNTYPED_SFIELD(sfBatchSigner,            OBJECT,    35)
 | 
			
		||||
UNTYPED_SFIELD(sfBook,                   OBJECT,    36)
 | 
			
		||||
UNTYPED_SFIELD(sfCounterpartySignature,  OBJECT,    37, SField::sMD_Default, SField::notSigning)
 | 
			
		||||
 | 
			
		||||
// array of objects (common)
 | 
			
		||||
// ARRAY/1 is reserved for end of array
 | 
			
		||||
@@ -405,3 +440,5 @@ UNTYPED_SFIELD(sfAcceptedCredentials,    ARRAY,     28)
 | 
			
		||||
UNTYPED_SFIELD(sfPermissions,            ARRAY,     29)
 | 
			
		||||
UNTYPED_SFIELD(sfRawTransactions,        ARRAY,     30)
 | 
			
		||||
UNTYPED_SFIELD(sfBatchSigners,           ARRAY,     31, SField::sMD_Default, SField::notSigning)
 | 
			
		||||
 | 
			
		||||
// clang-format on
 | 
			
		||||
 
 | 
			
		||||
@@ -316,7 +316,7 @@ TRANSACTION(ttTRUST_SET, 20, TrustSet,
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
 | 
			
		||||
    Delegation::notDelegatable,
 | 
			
		||||
    uint256{},
 | 
			
		||||
    featureDeletableAccounts,
 | 
			
		||||
    mustDeleteAcct,
 | 
			
		||||
    ({
 | 
			
		||||
    {sfDestination, soeREQUIRED},
 | 
			
		||||
@@ -332,7 +332,7 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureNonFungibleTokensV1,
 | 
			
		||||
    uint256{},
 | 
			
		||||
    changeNFTCounts,
 | 
			
		||||
    ({
 | 
			
		||||
    {sfNFTokenTaxon, soeREQUIRED},
 | 
			
		||||
@@ -350,7 +350,7 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureNonFungibleTokensV1,
 | 
			
		||||
    uint256{},
 | 
			
		||||
    changeNFTCounts,
 | 
			
		||||
    ({
 | 
			
		||||
    {sfNFTokenID, soeREQUIRED},
 | 
			
		||||
@@ -363,7 +363,7 @@ TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureNonFungibleTokensV1,
 | 
			
		||||
    uint256{},
 | 
			
		||||
    noPriv,
 | 
			
		||||
    ({
 | 
			
		||||
    {sfNFTokenID, soeREQUIRED},
 | 
			
		||||
@@ -379,7 +379,7 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureNonFungibleTokensV1,
 | 
			
		||||
    uint256{},
 | 
			
		||||
    noPriv,
 | 
			
		||||
    ({
 | 
			
		||||
    {sfNFTokenOffers, soeREQUIRED},
 | 
			
		||||
@@ -391,7 +391,7 @@ TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureNonFungibleTokensV1,
 | 
			
		||||
    uint256{},
 | 
			
		||||
    noPriv,
 | 
			
		||||
    ({
 | 
			
		||||
    {sfNFTokenBuyOffer, soeOPTIONAL},
 | 
			
		||||
@@ -837,7 +837,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
 | 
			
		||||
    Delegation::notDelegatable,
 | 
			
		||||
    featurePermissionDelegation,
 | 
			
		||||
    featurePermissionDelegationV1_1,
 | 
			
		||||
    noPriv,
 | 
			
		||||
    ({
 | 
			
		||||
    {sfAuthorize, soeREQUIRED},
 | 
			
		||||
@@ -944,6 +944,139 @@ TRANSACTION(ttBATCH, 71, Batch,
 | 
			
		||||
    {sfBatchSigners, soeOPTIONAL},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** Reserve 72-73 for future Vault-related transactions */
 | 
			
		||||
 | 
			
		||||
/** This transaction creates and updates a Loan Broker */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanBrokerSet.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    createPseudoAcct | mayAuthorizeMPT, ({
 | 
			
		||||
    {sfVaultID, soeREQUIRED},
 | 
			
		||||
    {sfLoanBrokerID, soeOPTIONAL},
 | 
			
		||||
    {sfData, soeOPTIONAL},
 | 
			
		||||
    {sfManagementFeeRate, soeOPTIONAL},
 | 
			
		||||
    {sfDebtMaximum, soeOPTIONAL},
 | 
			
		||||
    {sfCoverRateMinimum, soeOPTIONAL},
 | 
			
		||||
    {sfCoverRateLiquidation, soeOPTIONAL},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This transaction deletes a Loan Broker */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanBrokerDelete.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    mustDeleteAcct | mayAuthorizeMPT, ({
 | 
			
		||||
    {sfLoanBrokerID, soeREQUIRED},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This transaction deposits First Loss Capital into a Loan Broker */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanBrokerCoverDeposit.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    noPriv, ({
 | 
			
		||||
    {sfLoanBrokerID, soeREQUIRED},
 | 
			
		||||
    {sfAmount, soeREQUIRED, soeMPTSupported},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This transaction withdraws First Loss Capital from a Loan Broker */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanBrokerCoverWithdraw.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    mayAuthorizeMPT, ({
 | 
			
		||||
    {sfLoanBrokerID, soeREQUIRED},
 | 
			
		||||
    {sfAmount, soeREQUIRED, soeMPTSupported},
 | 
			
		||||
    {sfDestination, soeOPTIONAL},
 | 
			
		||||
    {sfDestinationTag, soeOPTIONAL},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This transaction claws back First Loss Capital from a Loan Broker to
 | 
			
		||||
    the issuer of the capital */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanBrokerCoverClawback.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    noPriv, ({
 | 
			
		||||
    {sfLoanBrokerID, soeOPTIONAL},
 | 
			
		||||
    {sfAmount, soeOPTIONAL, soeMPTSupported},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This transaction creates a Loan */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanSet.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_SET, 80, LoanSet,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    mayAuthorizeMPT | mustModifyVault, ({
 | 
			
		||||
    {sfLoanBrokerID, soeREQUIRED},
 | 
			
		||||
    {sfData, soeOPTIONAL},
 | 
			
		||||
    {sfCounterparty, soeOPTIONAL},
 | 
			
		||||
    {sfCounterpartySignature, soeOPTIONAL},
 | 
			
		||||
    {sfLoanOriginationFee, soeOPTIONAL},
 | 
			
		||||
    {sfLoanServiceFee, soeOPTIONAL},
 | 
			
		||||
    {sfLatePaymentFee, soeOPTIONAL},
 | 
			
		||||
    {sfClosePaymentFee, soeOPTIONAL},
 | 
			
		||||
    {sfOverpaymentFee, soeOPTIONAL},
 | 
			
		||||
    {sfInterestRate, soeOPTIONAL},
 | 
			
		||||
    {sfLateInterestRate, soeOPTIONAL},
 | 
			
		||||
    {sfCloseInterestRate, soeOPTIONAL},
 | 
			
		||||
    {sfOverpaymentInterestRate, soeOPTIONAL},
 | 
			
		||||
    {sfPrincipalRequested, soeREQUIRED},
 | 
			
		||||
    {sfPaymentTotal, soeOPTIONAL},
 | 
			
		||||
    {sfPaymentInterval, soeOPTIONAL},
 | 
			
		||||
    {sfGracePeriod, soeOPTIONAL},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This transaction deletes an existing Loan */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanDelete.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_DELETE, 81, LoanDelete,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    noPriv, ({
 | 
			
		||||
    {sfLoanID, soeREQUIRED},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This transaction is used to change the delinquency status of an existing Loan */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanManage.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    // All of the LoanManage options will modify the vault, but the
 | 
			
		||||
    // transaction can succeed without options, essentially making it
 | 
			
		||||
    // a noop.
 | 
			
		||||
    mayModifyVault, ({
 | 
			
		||||
    {sfLoanID, soeREQUIRED},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** The Borrower uses this transaction to make a Payment on the Loan. */
 | 
			
		||||
#if TRANSACTION_INCLUDE
 | 
			
		||||
#   include <xrpld/app/tx/detail/LoanPay.h>
 | 
			
		||||
#endif
 | 
			
		||||
TRANSACTION(ttLOAN_PAY, 84, LoanPay,
 | 
			
		||||
    Delegation::delegatable,
 | 
			
		||||
    featureLendingProtocol,
 | 
			
		||||
    mayAuthorizeMPT | mustModifyVault, ({
 | 
			
		||||
    {sfLoanID, soeREQUIRED},
 | 
			
		||||
    {sfAmount, soeREQUIRED, soeMPTSupported},
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
/** This system-generated transaction type is used to update the status of the various amendments.
 | 
			
		||||
 | 
			
		||||
    For details, see: https://xrpl.org/amendments.html
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,8 @@ JSS(BaseAsset);            // in: Oracle
 | 
			
		||||
JSS(BidMax);               // in: AMM Bid
 | 
			
		||||
JSS(BidMin);               // in: AMM Bid
 | 
			
		||||
JSS(ClearFlag);            // field.
 | 
			
		||||
JSS(Counterparty);         // field.
 | 
			
		||||
JSS(CounterpartySignature);// field.
 | 
			
		||||
JSS(DeliverMax);           // out: alias to Amount
 | 
			
		||||
JSS(DeliverMin);           // in: TransactionSign
 | 
			
		||||
JSS(Destination);          // in: TransactionSign; field.
 | 
			
		||||
@@ -392,6 +394,8 @@ JSS(load_factor_local);       // out: NetworkOPs
 | 
			
		||||
JSS(load_factor_net);         // out: NetworkOPs
 | 
			
		||||
JSS(load_factor_server);      // out: NetworkOPs
 | 
			
		||||
JSS(load_fee);                // out: LoadFeeTrackImp, NetworkOPs
 | 
			
		||||
JSS(loan_broker_id);          // in: LedgerEntry
 | 
			
		||||
JSS(loan_seq);                // in: LedgerEntry
 | 
			
		||||
JSS(local);                   // out: resource/Logic.h
 | 
			
		||||
JSS(local_txs);               // out: GetCounts
 | 
			
		||||
JSS(local_static_keys);       // out: ValidatorList
 | 
			
		||||
@@ -504,6 +508,7 @@ JSS(propose_seq);             // out: LedgerPropose
 | 
			
		||||
JSS(proposers);               // out: NetworkOPs, LedgerConsensus
 | 
			
		||||
JSS(protocol);                // out: NetworkOPs, PeerImp
 | 
			
		||||
JSS(proxied);                 // out: RPC ping
 | 
			
		||||
JSS(pseudo_account);          // out: AccountInfo
 | 
			
		||||
JSS(pubkey_node);             // out: NetworkOPs
 | 
			
		||||
JSS(pubkey_publisher);        // out: ValidatorList
 | 
			
		||||
JSS(pubkey_validator);        // out: NetworkOPs, ValidatorList
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,10 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_FAMILY_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_FAMILY_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Database.h>
 | 
			
		||||
#include <xrpld/shamap/FullBelowCache.h>
 | 
			
		||||
#include <xrpld/shamap/TreeNodeCache.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/beast/utility/Journal.h>
 | 
			
		||||
#include <xrpl/nodestore/Database.h>
 | 
			
		||||
#include <xrpl/shamap/FullBelowCache.h>
 | 
			
		||||
#include <xrpl/shamap/TreeNodeCache.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
@@ -20,21 +20,19 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAP_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAP_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Database.h>
 | 
			
		||||
#include <xrpld/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpld/shamap/Family.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapAddNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapLeafNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapMissingNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
#include <xrpld/shamap/TreeNodeCache.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.h>
 | 
			
		||||
#include <xrpl/basics/UnorderedContainers.h>
 | 
			
		||||
#include <xrpl/beast/utility/Journal.h>
 | 
			
		||||
#include <xrpl/beast/utility/instrumentation.h>
 | 
			
		||||
#include <xrpl/nodestore/Database.h>
 | 
			
		||||
#include <xrpl/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpl/shamap/Family.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapAddNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapLeafNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapMissingNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <stack>
 | 
			
		||||
@@ -20,12 +20,11 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPACCOUNTSTATELEAFNODE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPACCOUNTSTATELEAFNODE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapLeafNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/CountedObject.h>
 | 
			
		||||
#include <xrpl/protocol/HashPrefix.h>
 | 
			
		||||
#include <xrpl/protocol/digest.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapLeafNode.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -20,10 +20,9 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPINNERNODE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPINNERNODE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapNodeID.h>
 | 
			
		||||
#include <xrpld/shamap/detail/TaggedPointer.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapNodeID.h>
 | 
			
		||||
#include <xrpl/shamap/detail/TaggedPointer.h>
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -20,8 +20,8 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPLEAFNODE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPLEAFNODE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
@@ -20,9 +20,8 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPMISSINGNODE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPMISSINGNODE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/base_uint.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <iosfwd>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPSYNCFILTER_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPSYNCFILTER_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <optional>
 | 
			
		||||
 | 
			
		||||
@@ -20,13 +20,12 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPTREENODE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPTREENODE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapNodeID.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.h>
 | 
			
		||||
#include <xrpl/basics/IntrusiveRefCounts.h>
 | 
			
		||||
#include <xrpl/basics/SHAMapHash.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapNodeID.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -20,12 +20,11 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPTXLEAFNODE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPTXLEAFNODE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapLeafNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/CountedObject.h>
 | 
			
		||||
#include <xrpl/protocol/HashPrefix.h>
 | 
			
		||||
#include <xrpl/protocol/digest.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapLeafNode.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -20,12 +20,11 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_SHAMAPLEAFTXPLUSMETANODE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_SHAMAPLEAFTXPLUSMETANODE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapLeafNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/CountedObject.h>
 | 
			
		||||
#include <xrpl/protocol/HashPrefix.h>
 | 
			
		||||
#include <xrpl/protocol/digest.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapItem.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapLeafNode.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -20,10 +20,9 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_TREENODECACHE_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_TREENODECACHE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.h>
 | 
			
		||||
#include <xrpl/basics/TaggedCache.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -20,9 +20,8 @@
 | 
			
		||||
#ifndef RIPPLE_SHAMAP_TAGGEDPOINTER_H_INCLUDED
 | 
			
		||||
#define RIPPLE_SHAMAP_TAGGEDPOINTER_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
@@ -17,10 +17,9 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpld/shamap/detail/TaggedPointer.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/ByteUtilities.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpl/shamap/detail/TaggedPointer.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/pool/pool_alloc.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +43,8 @@ namespace ripple {
 | 
			
		||||
 | 
			
		||||
thread_local Number::rounding_mode Number::mode_ = Number::to_nearest;
 | 
			
		||||
 | 
			
		||||
Number const Number::zero{};
 | 
			
		||||
 | 
			
		||||
Number::rounding_mode
 | 
			
		||||
Number::getround()
 | 
			
		||||
{
 | 
			
		||||
@@ -93,6 +95,18 @@ public:
 | 
			
		||||
    // tie, round towards even.
 | 
			
		||||
    int
 | 
			
		||||
    round() noexcept;
 | 
			
		||||
 | 
			
		||||
    // Modify the result to the correctly rounded value
 | 
			
		||||
    void
 | 
			
		||||
    doRoundUp(rep& mantissa, int& exponent, std::string location);
 | 
			
		||||
 | 
			
		||||
    // Modify the result to the correctly rounded value
 | 
			
		||||
    void
 | 
			
		||||
    doRoundDown(rep& mantissa, int& exponent);
 | 
			
		||||
 | 
			
		||||
    // Modify the result to the correctly rounded value
 | 
			
		||||
    void
 | 
			
		||||
    doRound(rep& drops);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
inline void
 | 
			
		||||
@@ -170,6 +184,61 @@ Number::Guard::round() noexcept
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Number::Guard::doRoundUp(rep& mantissa, int& exponent, std::string location)
 | 
			
		||||
{
 | 
			
		||||
    auto r = round();
 | 
			
		||||
    if (r == 1 || (r == 0 && (mantissa & 1) == 1))
 | 
			
		||||
    {
 | 
			
		||||
        ++mantissa;
 | 
			
		||||
        if (mantissa > maxMantissa)
 | 
			
		||||
        {
 | 
			
		||||
            mantissa /= 10;
 | 
			
		||||
            ++exponent;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (exponent < minExponent)
 | 
			
		||||
    {
 | 
			
		||||
        mantissa = 0;
 | 
			
		||||
        exponent = Number{}.exponent_;
 | 
			
		||||
    }
 | 
			
		||||
    if (exponent > maxExponent)
 | 
			
		||||
        throw std::overflow_error(location);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Number::Guard::doRoundDown(rep& mantissa, int& exponent)
 | 
			
		||||
{
 | 
			
		||||
    auto r = round();
 | 
			
		||||
    if (r == 1 || (r == 0 && (mantissa & 1) == 1))
 | 
			
		||||
    {
 | 
			
		||||
        --mantissa;
 | 
			
		||||
        if (mantissa < minMantissa)
 | 
			
		||||
        {
 | 
			
		||||
            mantissa *= 10;
 | 
			
		||||
            --exponent;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (exponent < minExponent)
 | 
			
		||||
    {
 | 
			
		||||
        mantissa = 0;
 | 
			
		||||
        exponent = Number{}.exponent_;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Modify the result to the correctly rounded value
 | 
			
		||||
void
 | 
			
		||||
Number::Guard::doRound(rep& drops)
 | 
			
		||||
{
 | 
			
		||||
    auto r = round();
 | 
			
		||||
    if (r == 1 || (r == 0 && (drops & 1) == 1))
 | 
			
		||||
    {
 | 
			
		||||
        ++drops;
 | 
			
		||||
    }
 | 
			
		||||
    if (is_negative())
 | 
			
		||||
        drops = -drops;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Number
 | 
			
		||||
 | 
			
		||||
constexpr Number one{1000000000000000, -15, Number::unchecked{}};
 | 
			
		||||
@@ -209,18 +278,7 @@ Number::normalize()
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto r = g.round();
 | 
			
		||||
    if (r == 1 || (r == 0 && (mantissa_ & 1) == 1))
 | 
			
		||||
    {
 | 
			
		||||
        ++mantissa_;
 | 
			
		||||
        if (mantissa_ > maxMantissa)
 | 
			
		||||
        {
 | 
			
		||||
            mantissa_ /= 10;
 | 
			
		||||
            ++exponent_;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (exponent_ > maxExponent)
 | 
			
		||||
        throw std::overflow_error("Number::normalize 2");
 | 
			
		||||
    g.doRoundUp(mantissa_, exponent_, "Number::normalize 2");
 | 
			
		||||
 | 
			
		||||
    if (negative)
 | 
			
		||||
        mantissa_ = -mantissa_;
 | 
			
		||||
@@ -292,18 +350,7 @@ Number::operator+=(Number const& y)
 | 
			
		||||
            xm /= 10;
 | 
			
		||||
            ++xe;
 | 
			
		||||
        }
 | 
			
		||||
        auto r = g.round();
 | 
			
		||||
        if (r == 1 || (r == 0 && (xm & 1) == 1))
 | 
			
		||||
        {
 | 
			
		||||
            ++xm;
 | 
			
		||||
            if (xm > maxMantissa)
 | 
			
		||||
            {
 | 
			
		||||
                xm /= 10;
 | 
			
		||||
                ++xe;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (xe > maxExponent)
 | 
			
		||||
            throw std::overflow_error("Number::addition overflow");
 | 
			
		||||
        g.doRoundUp(xm, xe, "Number::addition overflow");
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
@@ -323,21 +370,7 @@ Number::operator+=(Number const& y)
 | 
			
		||||
            xm -= g.pop();
 | 
			
		||||
            --xe;
 | 
			
		||||
        }
 | 
			
		||||
        auto r = g.round();
 | 
			
		||||
        if (r == 1 || (r == 0 && (xm & 1) == 1))
 | 
			
		||||
        {
 | 
			
		||||
            --xm;
 | 
			
		||||
            if (xm < minMantissa)
 | 
			
		||||
            {
 | 
			
		||||
                xm *= 10;
 | 
			
		||||
                --xe;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (xe < minExponent)
 | 
			
		||||
        {
 | 
			
		||||
            xm = 0;
 | 
			
		||||
            xe = Number{}.exponent_;
 | 
			
		||||
        }
 | 
			
		||||
        g.doRoundDown(xm, xe);
 | 
			
		||||
    }
 | 
			
		||||
    mantissa_ = xm * xn;
 | 
			
		||||
    exponent_ = xe;
 | 
			
		||||
@@ -417,25 +450,10 @@ Number::operator*=(Number const& y)
 | 
			
		||||
    }
 | 
			
		||||
    xm = static_cast<rep>(zm);
 | 
			
		||||
    xe = ze;
 | 
			
		||||
    auto r = g.round();
 | 
			
		||||
    if (r == 1 || (r == 0 && (xm & 1) == 1))
 | 
			
		||||
    {
 | 
			
		||||
        ++xm;
 | 
			
		||||
        if (xm > maxMantissa)
 | 
			
		||||
        {
 | 
			
		||||
            xm /= 10;
 | 
			
		||||
            ++xe;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (xe < minExponent)
 | 
			
		||||
    {
 | 
			
		||||
        xm = 0;
 | 
			
		||||
        xe = Number{}.exponent_;
 | 
			
		||||
    }
 | 
			
		||||
    if (xe > maxExponent)
 | 
			
		||||
        throw std::overflow_error(
 | 
			
		||||
            "Number::multiplication overflow : exponent is " +
 | 
			
		||||
            std::to_string(xe));
 | 
			
		||||
    g.doRoundUp(
 | 
			
		||||
        xm,
 | 
			
		||||
        xe,
 | 
			
		||||
        "Number::multiplication overflow : exponent is " + std::to_string(xe));
 | 
			
		||||
    mantissa_ = xm * zn;
 | 
			
		||||
    exponent_ = xe;
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
@@ -500,13 +518,7 @@ Number::operator rep() const
 | 
			
		||||
                throw std::overflow_error("Number::operator rep() overflow");
 | 
			
		||||
            drops *= 10;
 | 
			
		||||
        }
 | 
			
		||||
        auto r = g.round();
 | 
			
		||||
        if (r == 1 || (r == 0 && (drops & 1) == 1))
 | 
			
		||||
        {
 | 
			
		||||
            ++drops;
 | 
			
		||||
        }
 | 
			
		||||
        if (g.is_negative())
 | 
			
		||||
            drops = -drops;
 | 
			
		||||
        g.doRound(drops);
 | 
			
		||||
    }
 | 
			
		||||
    return drops;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1001,6 +1001,12 @@ Value::isMember(std::string const& key) const
 | 
			
		||||
    return isMember(key.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
Value::isMember(StaticString const& key) const
 | 
			
		||||
{
 | 
			
		||||
    return isMember(key.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Value::Members
 | 
			
		||||
Value::getMemberNames() const
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -126,10 +126,10 @@ ApplyStateTable::apply(
 | 
			
		||||
    std::optional<TxMeta> metadata;
 | 
			
		||||
    if (!to.open() || isDryRun)
 | 
			
		||||
    {
 | 
			
		||||
        TxMeta meta(tx.getTransactionID(), to.seq(), parentBatchId);
 | 
			
		||||
        TxMeta meta(tx.getTransactionID(), to.seq());
 | 
			
		||||
 | 
			
		||||
        if (deliver)
 | 
			
		||||
            meta.setDeliveredAmount(*deliver);
 | 
			
		||||
        meta.setDeliveredAmount(deliver);
 | 
			
		||||
        meta.setParentBatchID(parentBatchId);
 | 
			
		||||
 | 
			
		||||
        Mods newMod;
 | 
			
		||||
        for (auto& item : items_)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,73 +27,92 @@
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
std::optional<std::uint64_t>
 | 
			
		||||
ApplyView::dirAdd(
 | 
			
		||||
    bool preserveOrder,
 | 
			
		||||
namespace directory {
 | 
			
		||||
 | 
			
		||||
std::uint64_t
 | 
			
		||||
createRoot(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    Keylet const& directory,
 | 
			
		||||
    uint256 const& key,
 | 
			
		||||
    std::function<void(std::shared_ptr<SLE> const&)> const& describe)
 | 
			
		||||
{
 | 
			
		||||
    auto root = peek(directory);
 | 
			
		||||
    auto newRoot = std::make_shared<SLE>(directory);
 | 
			
		||||
    newRoot->setFieldH256(sfRootIndex, directory.key);
 | 
			
		||||
    describe(newRoot);
 | 
			
		||||
 | 
			
		||||
    if (!root)
 | 
			
		||||
    {
 | 
			
		||||
        // No root, make it.
 | 
			
		||||
        root = std::make_shared<SLE>(directory);
 | 
			
		||||
        root->setFieldH256(sfRootIndex, directory.key);
 | 
			
		||||
        describe(root);
 | 
			
		||||
    STVector256 v;
 | 
			
		||||
    v.push_back(key);
 | 
			
		||||
    newRoot->setFieldV256(sfIndexes, v);
 | 
			
		||||
 | 
			
		||||
        STVector256 v;
 | 
			
		||||
        v.push_back(key);
 | 
			
		||||
        root->setFieldV256(sfIndexes, v);
 | 
			
		||||
    view.insert(newRoot);
 | 
			
		||||
    return std::uint64_t{0};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
        insert(root);
 | 
			
		||||
        return std::uint64_t{0};
 | 
			
		||||
    }
 | 
			
		||||
auto
 | 
			
		||||
findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start)
 | 
			
		||||
{
 | 
			
		||||
    std::uint64_t page = start->getFieldU64(sfIndexPrevious);
 | 
			
		||||
 | 
			
		||||
    std::uint64_t page = root->getFieldU64(sfIndexPrevious);
 | 
			
		||||
 | 
			
		||||
    auto node = root;
 | 
			
		||||
    auto node = start;
 | 
			
		||||
 | 
			
		||||
    if (page)
 | 
			
		||||
    {
 | 
			
		||||
        node = peek(keylet::page(directory, page));
 | 
			
		||||
        node = view.peek(keylet::page(directory, page));
 | 
			
		||||
        if (!node)
 | 
			
		||||
            LogicError("Directory chain: root back-pointer broken.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto indexes = node->getFieldV256(sfIndexes);
 | 
			
		||||
    return std::make_tuple(page, node, indexes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    // If there's space, we use it:
 | 
			
		||||
    if (indexes.size() < dirNodeMaxEntries)
 | 
			
		||||
std::uint64_t
 | 
			
		||||
insertKey(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    SLE::ref node,
 | 
			
		||||
    std::uint64_t page,
 | 
			
		||||
    bool preserveOrder,
 | 
			
		||||
    STVector256& indexes,
 | 
			
		||||
    uint256 const& key)
 | 
			
		||||
{
 | 
			
		||||
    if (preserveOrder)
 | 
			
		||||
    {
 | 
			
		||||
        if (preserveOrder)
 | 
			
		||||
        {
 | 
			
		||||
            if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
 | 
			
		||||
                LogicError("dirInsert: double insertion");
 | 
			
		||||
        if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
 | 
			
		||||
            LogicError("dirInsert: double insertion");
 | 
			
		||||
 | 
			
		||||
            indexes.push_back(key);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // We can't be sure if this page is already sorted because
 | 
			
		||||
            // it may be a legacy page we haven't yet touched. Take
 | 
			
		||||
            // the time to sort it.
 | 
			
		||||
            std::sort(indexes.begin(), indexes.end());
 | 
			
		||||
        indexes.push_back(key);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        // We can't be sure if this page is already sorted because
 | 
			
		||||
        // it may be a legacy page we haven't yet touched. Take
 | 
			
		||||
        // the time to sort it.
 | 
			
		||||
        std::sort(indexes.begin(), indexes.end());
 | 
			
		||||
 | 
			
		||||
            auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
 | 
			
		||||
        auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
 | 
			
		||||
 | 
			
		||||
            if (pos != indexes.end() && key == *pos)
 | 
			
		||||
                LogicError("dirInsert: double insertion");
 | 
			
		||||
        if (pos != indexes.end() && key == *pos)
 | 
			
		||||
            LogicError("dirInsert: double insertion");
 | 
			
		||||
 | 
			
		||||
            indexes.insert(pos, key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        node->setFieldV256(sfIndexes, indexes);
 | 
			
		||||
        update(node);
 | 
			
		||||
        return page;
 | 
			
		||||
        indexes.insert(pos, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    node->setFieldV256(sfIndexes, indexes);
 | 
			
		||||
    view.update(node);
 | 
			
		||||
    return page;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<std::uint64_t>
 | 
			
		||||
insertPage(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    std::uint64_t page,
 | 
			
		||||
    SLE::pointer node,
 | 
			
		||||
    std::uint64_t nextPage,
 | 
			
		||||
    SLE::ref next,
 | 
			
		||||
    uint256 const& key,
 | 
			
		||||
    Keylet const& directory,
 | 
			
		||||
    std::function<void(std::shared_ptr<SLE> const&)> const& describe)
 | 
			
		||||
{
 | 
			
		||||
    // We rely on modulo arithmetic of unsigned integers (guaranteed in
 | 
			
		||||
    // [basic.fundamental] paragraph 2) to detect page representation overflow.
 | 
			
		||||
    // For signed integers this would be UB, hence static_assert here.
 | 
			
		||||
@@ -107,20 +126,20 @@ ApplyView::dirAdd(
 | 
			
		||||
    // Check whether we're out of pages.
 | 
			
		||||
    if (page == 0)
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    if (!rules().enabled(fixDirectoryLimit) &&
 | 
			
		||||
    if (!view.rules().enabled(fixDirectoryLimit) &&
 | 
			
		||||
        page >= dirNodeMaxPages)  // Old pages limit
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
 | 
			
		||||
    // We are about to create a new node; we'll link it to
 | 
			
		||||
    // the chain first:
 | 
			
		||||
    node->setFieldU64(sfIndexNext, page);
 | 
			
		||||
    update(node);
 | 
			
		||||
    view.update(node);
 | 
			
		||||
 | 
			
		||||
    root->setFieldU64(sfIndexPrevious, page);
 | 
			
		||||
    update(root);
 | 
			
		||||
    next->setFieldU64(sfIndexPrevious, page);
 | 
			
		||||
    view.update(next);
 | 
			
		||||
 | 
			
		||||
    // Insert the new key:
 | 
			
		||||
    indexes.clear();
 | 
			
		||||
    STVector256 indexes;
 | 
			
		||||
    indexes.push_back(key);
 | 
			
		||||
 | 
			
		||||
    node = std::make_shared<SLE>(keylet::page(directory, page));
 | 
			
		||||
@@ -131,12 +150,45 @@ ApplyView::dirAdd(
 | 
			
		||||
    // it's the default.
 | 
			
		||||
    if (page != 1)
 | 
			
		||||
        node->setFieldU64(sfIndexPrevious, page - 1);
 | 
			
		||||
    if (nextPage)
 | 
			
		||||
        node->setFieldU64(sfIndexNext, nextPage);
 | 
			
		||||
    describe(node);
 | 
			
		||||
    insert(node);
 | 
			
		||||
    view.insert(node);
 | 
			
		||||
 | 
			
		||||
    return page;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace directory
 | 
			
		||||
 | 
			
		||||
std::optional<std::uint64_t>
 | 
			
		||||
ApplyView::dirAdd(
 | 
			
		||||
    bool preserveOrder,
 | 
			
		||||
    Keylet const& directory,
 | 
			
		||||
    uint256 const& key,
 | 
			
		||||
    std::function<void(std::shared_ptr<SLE> const&)> const& describe)
 | 
			
		||||
{
 | 
			
		||||
    auto root = peek(directory);
 | 
			
		||||
 | 
			
		||||
    if (!root)
 | 
			
		||||
    {
 | 
			
		||||
        // No root, make it.
 | 
			
		||||
        return directory::createRoot(*this, directory, key, describe);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto [page, node, indexes] =
 | 
			
		||||
        directory::findPreviousPage(*this, directory, root);
 | 
			
		||||
 | 
			
		||||
    // If there's space, we use it:
 | 
			
		||||
    if (indexes.size() < dirNodeMaxEntries)
 | 
			
		||||
    {
 | 
			
		||||
        return directory::insertKey(
 | 
			
		||||
            *this, node, page, preserveOrder, indexes, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return directory::insertPage(
 | 
			
		||||
        *this, page, node, 0, root, key, directory, describe);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
ApplyView::emptyDirDelete(Keylet const& directory)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -383,6 +383,99 @@ isLPTokenFrozen(
 | 
			
		||||
        isFrozen(view, account, asset2.currency, asset2.account);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SLE::const_pointer
 | 
			
		||||
getLineIfUsable(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Currency const& currency,
 | 
			
		||||
    AccountID const& issuer,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    beast::Journal j)
 | 
			
		||||
{
 | 
			
		||||
    auto const sle = view.read(keylet::line(account, issuer, currency));
 | 
			
		||||
 | 
			
		||||
    if (!sle)
 | 
			
		||||
    {
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (zeroIfFrozen == fhZERO_IF_FROZEN)
 | 
			
		||||
    {
 | 
			
		||||
        if (isFrozen(view, account, currency, issuer) ||
 | 
			
		||||
            isDeepFrozen(view, account, currency, issuer))
 | 
			
		||||
        {
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken,
 | 
			
		||||
        // we need to check if the associated assets have been frozen
 | 
			
		||||
        if (view.rules().enabled(fixFrozenLPTokenTransfer))
 | 
			
		||||
        {
 | 
			
		||||
            auto const sleIssuer = view.read(keylet::account(issuer));
 | 
			
		||||
            if (!sleIssuer)
 | 
			
		||||
            {
 | 
			
		||||
                return nullptr;  // LCOV_EXCL_LINE
 | 
			
		||||
            }
 | 
			
		||||
            else if (sleIssuer->isFieldPresent(sfAMMID))
 | 
			
		||||
            {
 | 
			
		||||
                auto const sleAmm =
 | 
			
		||||
                    view.read(keylet::amm((*sleIssuer)[sfAMMID]));
 | 
			
		||||
 | 
			
		||||
                if (!sleAmm ||
 | 
			
		||||
                    isLPTokenFrozen(
 | 
			
		||||
                        view,
 | 
			
		||||
                        account,
 | 
			
		||||
                        (*sleAmm)[sfAsset].get<Issue>(),
 | 
			
		||||
                        (*sleAmm)[sfAsset2].get<Issue>()))
 | 
			
		||||
                {
 | 
			
		||||
                    return nullptr;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static STAmount
 | 
			
		||||
getTrustLineBalance(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    SLE::const_ref sle,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Currency const& currency,
 | 
			
		||||
    AccountID const& issuer,
 | 
			
		||||
    bool includeOppositeLimit,
 | 
			
		||||
    beast::Journal j)
 | 
			
		||||
{
 | 
			
		||||
    STAmount amount;
 | 
			
		||||
    if (sle)
 | 
			
		||||
    {
 | 
			
		||||
        amount = sle->getFieldAmount(sfBalance);
 | 
			
		||||
        bool const accountHigh = account > issuer;
 | 
			
		||||
        auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit;
 | 
			
		||||
        if (accountHigh)
 | 
			
		||||
        {
 | 
			
		||||
            // Put balance in account terms.
 | 
			
		||||
            amount.negate();
 | 
			
		||||
        }
 | 
			
		||||
        if (includeOppositeLimit)
 | 
			
		||||
        {
 | 
			
		||||
            amount += sle->getFieldAmount(oppositeField);
 | 
			
		||||
        }
 | 
			
		||||
        amount.setIssuer(issuer);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        amount.clear(Issue{currency, issuer});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JLOG(j.trace()) << "getTrustLineBalance:"
 | 
			
		||||
                    << " account=" << to_string(account)
 | 
			
		||||
                    << " amount=" << amount.getFullText();
 | 
			
		||||
 | 
			
		||||
    return view.balanceHook(account, issuer, amount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
STAmount
 | 
			
		||||
accountHolds(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
@@ -399,71 +492,10 @@ accountHolds(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // IOU: Return balance on trust line modulo freeze
 | 
			
		||||
    auto const sle = view.read(keylet::line(account, issuer, currency));
 | 
			
		||||
    auto const allowBalance = [&]() {
 | 
			
		||||
        if (!sle)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    SLE::const_pointer const sle =
 | 
			
		||||
        getLineIfUsable(view, account, currency, issuer, zeroIfFrozen, j);
 | 
			
		||||
 | 
			
		||||
        if (zeroIfFrozen == fhZERO_IF_FROZEN)
 | 
			
		||||
        {
 | 
			
		||||
            if (isFrozen(view, account, currency, issuer) ||
 | 
			
		||||
                isDeepFrozen(view, account, currency, issuer))
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken,
 | 
			
		||||
            // we need to check if the associated assets have been frozen
 | 
			
		||||
            if (view.rules().enabled(fixFrozenLPTokenTransfer))
 | 
			
		||||
            {
 | 
			
		||||
                auto const sleIssuer = view.read(keylet::account(issuer));
 | 
			
		||||
                if (!sleIssuer)
 | 
			
		||||
                {
 | 
			
		||||
                    return false;  // LCOV_EXCL_LINE
 | 
			
		||||
                }
 | 
			
		||||
                else if (sleIssuer->isFieldPresent(sfAMMID))
 | 
			
		||||
                {
 | 
			
		||||
                    auto const sleAmm =
 | 
			
		||||
                        view.read(keylet::amm((*sleIssuer)[sfAMMID]));
 | 
			
		||||
 | 
			
		||||
                    if (!sleAmm ||
 | 
			
		||||
                        isLPTokenFrozen(
 | 
			
		||||
                            view,
 | 
			
		||||
                            account,
 | 
			
		||||
                            (*sleAmm)[sfAsset].get<Issue>(),
 | 
			
		||||
                            (*sleAmm)[sfAsset2].get<Issue>()))
 | 
			
		||||
                    {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    if (allowBalance)
 | 
			
		||||
    {
 | 
			
		||||
        amount = sle->getFieldAmount(sfBalance);
 | 
			
		||||
        if (account > issuer)
 | 
			
		||||
        {
 | 
			
		||||
            // Put balance in account terms.
 | 
			
		||||
            amount.negate();
 | 
			
		||||
        }
 | 
			
		||||
        amount.setIssuer(issuer);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        amount.clear(Issue{currency, issuer});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JLOG(j.trace()) << "accountHolds:"
 | 
			
		||||
                    << " account=" << to_string(account)
 | 
			
		||||
                    << " amount=" << amount.getFullText();
 | 
			
		||||
 | 
			
		||||
    return view.balanceHook(account, issuer, amount);
 | 
			
		||||
    return getTrustLineBalance(view, sle, account, currency, issuer, false, j);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
STAmount
 | 
			
		||||
@@ -550,6 +582,96 @@ accountHolds(
 | 
			
		||||
        asset.value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Currency const& currency,
 | 
			
		||||
    AccountID const& issuer,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    beast::Journal j)
 | 
			
		||||
{
 | 
			
		||||
    if (isXRP(currency))
 | 
			
		||||
        return accountHolds(view, account, currency, issuer, zeroIfFrozen, j);
 | 
			
		||||
 | 
			
		||||
    if (account == issuer)
 | 
			
		||||
        // If the account is the issuer, then their limit is effectively
 | 
			
		||||
        // infinite
 | 
			
		||||
        return STAmount{
 | 
			
		||||
            Issue{currency, issuer}, STAmount::cMaxValue, STAmount::cMaxOffset};
 | 
			
		||||
 | 
			
		||||
    // IOU: Return balance on trust line modulo freeze
 | 
			
		||||
    SLE::const_pointer const sle =
 | 
			
		||||
        getLineIfUsable(view, account, currency, issuer, zeroIfFrozen, j);
 | 
			
		||||
 | 
			
		||||
    return getTrustLineBalance(view, sle, account, currency, issuer, true, j);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Issue const& issue,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    beast::Journal j)
 | 
			
		||||
{
 | 
			
		||||
    return accountCanSend(
 | 
			
		||||
        view, account, issue.currency, issue.account, zeroIfFrozen, j);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    MPTIssue const& mptIssue,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    AuthHandling zeroIfUnauthorized,
 | 
			
		||||
    beast::Journal j)
 | 
			
		||||
{
 | 
			
		||||
    if (account == mptIssue.getIssuer())
 | 
			
		||||
    {
 | 
			
		||||
        // if the account is the issuer, and the issuance exists, their limit is
 | 
			
		||||
        // the issuance limit minus the outstanding value
 | 
			
		||||
        auto const issuance =
 | 
			
		||||
            view.read(keylet::mptIssuance(mptIssue.getMptID()));
 | 
			
		||||
 | 
			
		||||
        if (!issuance)
 | 
			
		||||
        {
 | 
			
		||||
            return STAmount{mptIssue};
 | 
			
		||||
        }
 | 
			
		||||
        return STAmount{
 | 
			
		||||
            mptIssue,
 | 
			
		||||
            issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) -
 | 
			
		||||
                issuance->at(sfOutstandingAmount)};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return accountHolds(
 | 
			
		||||
        view, account, mptIssue, zeroIfFrozen, zeroIfUnauthorized, j);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] STAmount
 | 
			
		||||
accountCanSend(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& account,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    FreezeHandling zeroIfFrozen,
 | 
			
		||||
    AuthHandling zeroIfUnauthorized,
 | 
			
		||||
    beast::Journal j)
 | 
			
		||||
{
 | 
			
		||||
    return std::visit(
 | 
			
		||||
        [&](auto const& value) {
 | 
			
		||||
            if constexpr (std::is_same_v<
 | 
			
		||||
                              std::remove_cvref_t<decltype(value)>,
 | 
			
		||||
                              Issue>)
 | 
			
		||||
            {
 | 
			
		||||
                return accountCanSend(view, account, value, zeroIfFrozen, j);
 | 
			
		||||
            }
 | 
			
		||||
            return accountCanSend(
 | 
			
		||||
                view, account, value, zeroIfFrozen, zeroIfUnauthorized, j);
 | 
			
		||||
        },
 | 
			
		||||
        asset.value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
STAmount
 | 
			
		||||
accountFunds(
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
@@ -1055,13 +1177,17 @@ describeOwnerDir(AccountID const& account)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TER
 | 
			
		||||
dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr<SLE>& object)
 | 
			
		||||
dirLink(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& owner,
 | 
			
		||||
    std::shared_ptr<SLE>& object,
 | 
			
		||||
    SF_UINT64 const& node)
 | 
			
		||||
{
 | 
			
		||||
    auto const page = view.dirInsert(
 | 
			
		||||
        keylet::ownerDir(owner), object->key(), describeOwnerDir(owner));
 | 
			
		||||
    if (!page)
 | 
			
		||||
        return tecDIR_FULL;  // LCOV_EXCL_LINE
 | 
			
		||||
    object->setFieldU64(sfOwnerNode, *page);
 | 
			
		||||
    object->setFieldU64(node, *page);
 | 
			
		||||
    return tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1134,7 +1260,8 @@ createPseudoAccount(
 | 
			
		||||
    uint256 const& pseudoOwnerKey,
 | 
			
		||||
    SField const& ownerField)
 | 
			
		||||
{
 | 
			
		||||
    [[maybe_unused]] auto const& fields = getPseudoAccountFields();
 | 
			
		||||
    [[maybe_unused]]
 | 
			
		||||
    auto const& fields = getPseudoAccountFields();
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
        std::count_if(
 | 
			
		||||
            fields.begin(),
 | 
			
		||||
@@ -1156,9 +1283,10 @@ createPseudoAccount(
 | 
			
		||||
    // Pseudo-accounts can't submit transactions, so set the sequence number
 | 
			
		||||
    // to 0 to make them easier to spot and verify, and add an extra level
 | 
			
		||||
    // of protection.
 | 
			
		||||
    std::uint32_t const seqno =                        //
 | 
			
		||||
        view.rules().enabled(featureSingleAssetVault)  //
 | 
			
		||||
        ? 0                                            //
 | 
			
		||||
    std::uint32_t const seqno =                           //
 | 
			
		||||
        view.rules().enabled(featureSingleAssetVault) ||  //
 | 
			
		||||
            view.rules().enabled(featureLendingProtocol)  //
 | 
			
		||||
        ? 0                                               //
 | 
			
		||||
        : view.seq();
 | 
			
		||||
    account->setFieldU32(sfSequence, seqno);
 | 
			
		||||
    // Ignore reserves requirement, disable the master key, allow default
 | 
			
		||||
@@ -1212,6 +1340,64 @@ canAddHolding(ReadView const& view, Asset const& asset)
 | 
			
		||||
        asset.value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
 | 
			
		||||
{
 | 
			
		||||
    if (toSle == nullptr)
 | 
			
		||||
        return tecNO_DST;
 | 
			
		||||
 | 
			
		||||
    // The tag is basically account-specific information we don't
 | 
			
		||||
    // understand, but we can require someone to fill it in.
 | 
			
		||||
    if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag)
 | 
			
		||||
        return tecDST_TAG_NEEDED;  // Cannot send without a tag
 | 
			
		||||
 | 
			
		||||
    return tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
canWithdraw(
 | 
			
		||||
    AccountID const& from,
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& to,
 | 
			
		||||
    SLE::const_ref toSle,
 | 
			
		||||
    bool hasDestinationTag)
 | 
			
		||||
{
 | 
			
		||||
    if (auto const ret = checkDestinationAndTag(toSle, hasDestinationTag))
 | 
			
		||||
        return ret;
 | 
			
		||||
 | 
			
		||||
    if (from == to)
 | 
			
		||||
        return tesSUCCESS;
 | 
			
		||||
 | 
			
		||||
    if (toSle->isFlag(lsfDepositAuth))
 | 
			
		||||
    {
 | 
			
		||||
        if (!view.exists(keylet::depositPreauth(to, from)))
 | 
			
		||||
            return tecNO_PERMISSION;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
canWithdraw(
 | 
			
		||||
    AccountID const& from,
 | 
			
		||||
    ReadView const& view,
 | 
			
		||||
    AccountID const& to,
 | 
			
		||||
    bool hasDestinationTag)
 | 
			
		||||
{
 | 
			
		||||
    auto const toSle = view.read(keylet::account(to));
 | 
			
		||||
 | 
			
		||||
    return canWithdraw(from, view, to, toSle, hasDestinationTag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
canWithdraw(ReadView const& view, STTx const& tx)
 | 
			
		||||
{
 | 
			
		||||
    auto const from = tx[sfAccount];
 | 
			
		||||
    auto const to = tx[~sfDestination].value_or(from);
 | 
			
		||||
 | 
			
		||||
    return canWithdraw(from, view, to, tx.isFieldPresent(sfDestinationTag));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] TER
 | 
			
		||||
addEmptyHolding(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
@@ -1220,8 +1406,8 @@ addEmptyHolding(
 | 
			
		||||
    Issue const& issue,
 | 
			
		||||
    beast::Journal journal)
 | 
			
		||||
{
 | 
			
		||||
    // Every account can hold XRP.
 | 
			
		||||
    if (issue.native())
 | 
			
		||||
    // Every account can hold XRP. An issuer can issue directly.
 | 
			
		||||
    if (issue.native() || accountID == issue.getIssuer())
 | 
			
		||||
        return tesSUCCESS;
 | 
			
		||||
 | 
			
		||||
    auto const& issuerId = issue.getIssuer();
 | 
			
		||||
@@ -1282,6 +1468,8 @@ addEmptyHolding(
 | 
			
		||||
        return tefINTERNAL;  // LCOV_EXCL_LINE
 | 
			
		||||
    if (view.peek(keylet::mptoken(mptID, accountID)))
 | 
			
		||||
        return tecDUPLICATE;
 | 
			
		||||
    if (accountID == mptIssue.getIssuer())
 | 
			
		||||
        return tesSUCCESS;
 | 
			
		||||
 | 
			
		||||
    return authorizeMPToken(view, priorBalance, mptID, accountID, journal);
 | 
			
		||||
}
 | 
			
		||||
@@ -1345,6 +1533,18 @@ authorizeMPToken(
 | 
			
		||||
        if (priorBalance < reserveCreate)
 | 
			
		||||
            return tecINSUFFICIENT_RESERVE;
 | 
			
		||||
 | 
			
		||||
        // Defensive check before we attempt to create MPToken for the issuer
 | 
			
		||||
        auto const mpt = view.read(keylet::mptIssuance(mptIssuanceID));
 | 
			
		||||
        if (!mpt || mpt->getAccountID(sfIssuer) == account)
 | 
			
		||||
        {
 | 
			
		||||
            // LCOV_EXCL_START
 | 
			
		||||
            UNREACHABLE(
 | 
			
		||||
                "ripple::authorizeMPToken : invalid issuance or issuers token");
 | 
			
		||||
            if (view.rules().enabled(featureLendingProtocol))
 | 
			
		||||
                return tecINTERNAL;
 | 
			
		||||
            // LCOV_EXCL_STOP
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
 | 
			
		||||
        auto mptoken = std::make_shared<SLE>(mptokenKey);
 | 
			
		||||
        if (auto ter = dirLink(view, account, mptoken))
 | 
			
		||||
@@ -1420,6 +1620,14 @@ trustCreate(
 | 
			
		||||
 | 
			
		||||
    auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
 | 
			
		||||
    auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
 | 
			
		||||
    if (uLowAccountID == uHighAccountID)
 | 
			
		||||
    {
 | 
			
		||||
        // LCOV_EXCL_START
 | 
			
		||||
        UNREACHABLE("ripple::trustCreate : trust line to self");
 | 
			
		||||
        if (view.rules().enabled(featureLendingProtocol))
 | 
			
		||||
            return tecINTERNAL;
 | 
			
		||||
        // LCOV_EXCL_STOP
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
 | 
			
		||||
    view.insert(sleRippleState);
 | 
			
		||||
@@ -1910,6 +2118,87 @@ rippleSendIOU(
 | 
			
		||||
    return terResult;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send regardless of limits.
 | 
			
		||||
// --> receivers: Amount/currency/issuer to deliver to receivers.
 | 
			
		||||
// <-- saActual: Amount actually cost to sender.  Sender pays fees.
 | 
			
		||||
static TER
 | 
			
		||||
rippleSendMultiIOU(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& senderID,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    MultiplePaymentDestinations const& receivers,
 | 
			
		||||
    STAmount& actual,
 | 
			
		||||
    beast::Journal j,
 | 
			
		||||
    WaiveTransferFee waiveFee)
 | 
			
		||||
{
 | 
			
		||||
    auto const issuer = asset.getIssuer();
 | 
			
		||||
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
        !isXRP(senderID), "ripple::rippleSendMultiIOU : sender is not XRP");
 | 
			
		||||
 | 
			
		||||
    // These may diverge
 | 
			
		||||
    STAmount takeFromSender{asset};
 | 
			
		||||
    actual = takeFromSender;
 | 
			
		||||
 | 
			
		||||
    // Failures return immediately.
 | 
			
		||||
    for (auto const& r : receivers)
 | 
			
		||||
    {
 | 
			
		||||
        auto const& receiverID = r.first;
 | 
			
		||||
        STAmount amount{asset, r.second};
 | 
			
		||||
 | 
			
		||||
        /* If we aren't sending anything or if the sender is the same as the
 | 
			
		||||
         * receiver then we don't need to do anything.
 | 
			
		||||
         */
 | 
			
		||||
        if (!amount || (senderID == receiverID))
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        XRPL_ASSERT(
 | 
			
		||||
            !isXRP(receiverID),
 | 
			
		||||
            "ripple::rippleSendMultiIOU : receiver is not XRP");
 | 
			
		||||
 | 
			
		||||
        if (senderID == issuer || receiverID == issuer || issuer == noAccount())
 | 
			
		||||
        {
 | 
			
		||||
            // Direct send: redeeming IOUs and/or sending own IOUs.
 | 
			
		||||
            if (auto const ter = rippleCreditIOU(
 | 
			
		||||
                    view, senderID, receiverID, amount, false, j))
 | 
			
		||||
                return ter;
 | 
			
		||||
            actual += amount;
 | 
			
		||||
            // Do not add amount to takeFromSender, because rippleCreditIOU took
 | 
			
		||||
            // it.
 | 
			
		||||
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sending 3rd party IOUs: transit.
 | 
			
		||||
 | 
			
		||||
        // Calculate the amount to transfer accounting
 | 
			
		||||
        // for any transfer fees if the fee is not waived:
 | 
			
		||||
        STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
 | 
			
		||||
            ? amount
 | 
			
		||||
            : multiply(amount, transferRate(view, issuer));
 | 
			
		||||
        actual += actualSend;
 | 
			
		||||
        takeFromSender += actualSend;
 | 
			
		||||
 | 
			
		||||
        JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID)
 | 
			
		||||
                        << " - > " << to_string(receiverID)
 | 
			
		||||
                        << " : deliver=" << amount.getFullText()
 | 
			
		||||
                        << " cost=" << actual.getFullText();
 | 
			
		||||
 | 
			
		||||
        if (TER const terResult =
 | 
			
		||||
                rippleCreditIOU(view, issuer, receiverID, amount, true, j))
 | 
			
		||||
            return terResult;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (senderID != issuer && takeFromSender)
 | 
			
		||||
    {
 | 
			
		||||
        if (TER const terResult = rippleCreditIOU(
 | 
			
		||||
                view, senderID, issuer, takeFromSender, true, j))
 | 
			
		||||
            return terResult;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TER
 | 
			
		||||
accountSendIOU(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
@@ -2034,6 +2323,165 @@ accountSendIOU(
 | 
			
		||||
    return terResult;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TER
 | 
			
		||||
accountSendMultiIOU(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& senderID,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    MultiplePaymentDestinations const& receivers,
 | 
			
		||||
    beast::Journal j,
 | 
			
		||||
    WaiveTransferFee waiveFee)
 | 
			
		||||
{
 | 
			
		||||
    XRPL_ASSERT_PARTS(
 | 
			
		||||
        receivers.size() > 1,
 | 
			
		||||
        "ripple::accountSendMultiIOU",
 | 
			
		||||
        "multiple recipients provided");
 | 
			
		||||
 | 
			
		||||
    if (view.rules().enabled(fixAMMv1_1))
 | 
			
		||||
    {
 | 
			
		||||
        if (asset.holds<MPTIssue>())
 | 
			
		||||
        {
 | 
			
		||||
            return tecINTERNAL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        XRPL_ASSERT(
 | 
			
		||||
            !asset.holds<MPTIssue>(), "ripple::accountSendMultiIOU : not MPT");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!asset.native())
 | 
			
		||||
    {
 | 
			
		||||
        STAmount actual;
 | 
			
		||||
        JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID)
 | 
			
		||||
                        << " sending " << receivers.size() << " IOUs";
 | 
			
		||||
 | 
			
		||||
        return rippleSendMultiIOU(
 | 
			
		||||
            view, senderID, asset, receivers, actual, j, waiveFee);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* XRP send which does not check reserve and can do pure adjustment.
 | 
			
		||||
     * Note that sender or receiver may be null and this not a mistake; this
 | 
			
		||||
     * setup could be used during pathfinding and it is carefully controlled to
 | 
			
		||||
     * ensure that transfers are balanced.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    SLE::pointer sender = senderID != beast::zero
 | 
			
		||||
        ? view.peek(keylet::account(senderID))
 | 
			
		||||
        : SLE::pointer();
 | 
			
		||||
 | 
			
		||||
    if (auto stream = j.trace())
 | 
			
		||||
    {
 | 
			
		||||
        std::string sender_bal("-");
 | 
			
		||||
 | 
			
		||||
        if (sender)
 | 
			
		||||
            sender_bal = sender->getFieldAmount(sfBalance).getFullText();
 | 
			
		||||
 | 
			
		||||
        stream << "accountSendMultiIOU> " << to_string(senderID) << " ("
 | 
			
		||||
               << sender_bal << ") -> " << receivers.size() << " receivers.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Failures return immediately.
 | 
			
		||||
    STAmount takeFromSender{asset};
 | 
			
		||||
    for (auto const& r : receivers)
 | 
			
		||||
    {
 | 
			
		||||
        auto const& receiverID = r.first;
 | 
			
		||||
        STAmount amount{asset, r.second};
 | 
			
		||||
 | 
			
		||||
        takeFromSender += amount;
 | 
			
		||||
 | 
			
		||||
        if (view.rules().enabled(fixAMMv1_1))
 | 
			
		||||
        {
 | 
			
		||||
            if (amount < beast::zero)
 | 
			
		||||
            {
 | 
			
		||||
                return tecINTERNAL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            XRPL_ASSERT(
 | 
			
		||||
                amount >= beast::zero,
 | 
			
		||||
                "ripple::accountSendMultiIOU : minimum amount");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* If we aren't sending anything or if the sender is the same as the
 | 
			
		||||
         * receiver then we don't need to do anything.
 | 
			
		||||
         */
 | 
			
		||||
        if (!amount || (senderID == receiverID))
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        SLE::pointer receiver = receiverID != beast::zero
 | 
			
		||||
            ? view.peek(keylet::account(receiverID))
 | 
			
		||||
            : SLE::pointer();
 | 
			
		||||
 | 
			
		||||
        if (auto stream = j.trace())
 | 
			
		||||
        {
 | 
			
		||||
            std::string receiver_bal("-");
 | 
			
		||||
 | 
			
		||||
            if (receiver)
 | 
			
		||||
                receiver_bal =
 | 
			
		||||
                    receiver->getFieldAmount(sfBalance).getFullText();
 | 
			
		||||
 | 
			
		||||
            stream << "accountSendMultiIOU> " << to_string(senderID) << " -> "
 | 
			
		||||
                   << to_string(receiverID) << " (" << receiver_bal
 | 
			
		||||
                   << ") : " << amount.getFullText();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (receiver)
 | 
			
		||||
        {
 | 
			
		||||
            // Increment XRP balance.
 | 
			
		||||
            auto const rcvBal = receiver->getFieldAmount(sfBalance);
 | 
			
		||||
            receiver->setFieldAmount(sfBalance, rcvBal + amount);
 | 
			
		||||
            view.creditHook(xrpAccount(), receiverID, amount, -rcvBal);
 | 
			
		||||
 | 
			
		||||
            view.update(receiver);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (auto stream = j.trace())
 | 
			
		||||
        {
 | 
			
		||||
            std::string receiver_bal("-");
 | 
			
		||||
 | 
			
		||||
            if (receiver)
 | 
			
		||||
                receiver_bal =
 | 
			
		||||
                    receiver->getFieldAmount(sfBalance).getFullText();
 | 
			
		||||
 | 
			
		||||
            stream << "accountSendMultiIOU< " << to_string(senderID) << " -> "
 | 
			
		||||
                   << to_string(receiverID) << " (" << receiver_bal
 | 
			
		||||
                   << ") : " << amount.getFullText();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sender)
 | 
			
		||||
    {
 | 
			
		||||
        if (sender->getFieldAmount(sfBalance) < takeFromSender)
 | 
			
		||||
        {
 | 
			
		||||
            return TER{tecFAILED_PROCESSING};
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            auto const sndBal = sender->getFieldAmount(sfBalance);
 | 
			
		||||
            view.creditHook(senderID, xrpAccount(), takeFromSender, sndBal);
 | 
			
		||||
 | 
			
		||||
            // Decrement XRP balance.
 | 
			
		||||
            sender->setFieldAmount(sfBalance, sndBal - takeFromSender);
 | 
			
		||||
            view.update(sender);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (auto stream = j.trace())
 | 
			
		||||
    {
 | 
			
		||||
        std::string sender_bal("-");
 | 
			
		||||
        std::string receiver_bal("-");
 | 
			
		||||
 | 
			
		||||
        if (sender)
 | 
			
		||||
            sender_bal = sender->getFieldAmount(sfBalance).getFullText();
 | 
			
		||||
 | 
			
		||||
        stream << "accountSendMultiIOU< " << to_string(senderID) << " ("
 | 
			
		||||
               << sender_bal << ") -> " << receivers.size() << " receivers.";
 | 
			
		||||
    }
 | 
			
		||||
    return tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TER
 | 
			
		||||
rippleCreditMPT(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
@@ -2162,6 +2610,102 @@ rippleSendMPT(
 | 
			
		||||
    return rippleCreditMPT(view, uSenderID, issuer, saActual, j);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TER
 | 
			
		||||
rippleSendMultiMPT(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& senderID,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    MultiplePaymentDestinations const& receivers,
 | 
			
		||||
    STAmount& actual,
 | 
			
		||||
    beast::Journal j,
 | 
			
		||||
    WaiveTransferFee waiveFee)
 | 
			
		||||
{
 | 
			
		||||
    // Safe to get MPT since rippleSendMultiMPT is only called by
 | 
			
		||||
    // accountSendMultiMPT
 | 
			
		||||
    auto const issuer = asset.getIssuer();
 | 
			
		||||
 | 
			
		||||
    auto const sle =
 | 
			
		||||
        view.read(keylet::mptIssuance(asset.get<MPTIssue>().getMptID()));
 | 
			
		||||
    if (!sle)
 | 
			
		||||
        return tecOBJECT_NOT_FOUND;
 | 
			
		||||
 | 
			
		||||
    // These may diverge
 | 
			
		||||
    STAmount takeFromSender{asset};
 | 
			
		||||
    actual = takeFromSender;
 | 
			
		||||
 | 
			
		||||
    for (auto const& r : receivers)
 | 
			
		||||
    {
 | 
			
		||||
        auto const& receiverID = r.first;
 | 
			
		||||
        STAmount amount{asset, r.second};
 | 
			
		||||
 | 
			
		||||
        XRPL_ASSERT(
 | 
			
		||||
            senderID != receiverID,
 | 
			
		||||
            "ripple::rippleSendMultiMPT : sender is not receiver");
 | 
			
		||||
 | 
			
		||||
        XRPL_ASSERT(
 | 
			
		||||
            amount >= beast::zero,
 | 
			
		||||
            "ripple::rippleSendMultiMPT : minimum amount ");
 | 
			
		||||
 | 
			
		||||
        /* If we aren't sending anything or if the sender is the same as the
 | 
			
		||||
         * receiver then we don't need to do anything.
 | 
			
		||||
         */
 | 
			
		||||
        if (!amount || (senderID == receiverID))
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (senderID == issuer || receiverID == issuer)
 | 
			
		||||
        {
 | 
			
		||||
            // if sender is issuer, check that the new OutstandingAmount will
 | 
			
		||||
            // not exceed MaximumAmount
 | 
			
		||||
            if (senderID == issuer)
 | 
			
		||||
            {
 | 
			
		||||
                auto const sendAmount = amount.mpt().value();
 | 
			
		||||
                auto const maximumAmount =
 | 
			
		||||
                    sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
 | 
			
		||||
                if (sendAmount > maximumAmount - takeFromSender ||
 | 
			
		||||
                    sle->getFieldU64(sfOutstandingAmount) >
 | 
			
		||||
                        maximumAmount - sendAmount - takeFromSender)
 | 
			
		||||
                    return tecPATH_DRY;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Direct send: redeeming MPTs and/or sending own MPTs.
 | 
			
		||||
            if (auto const ter =
 | 
			
		||||
                    rippleCreditMPT(view, senderID, receiverID, amount, j))
 | 
			
		||||
                return ter;
 | 
			
		||||
            actual += amount;
 | 
			
		||||
            // Do not add amount to takeFromSender, because rippleCreditMPT took
 | 
			
		||||
            // it
 | 
			
		||||
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sending 3rd party MPTs: transit.
 | 
			
		||||
        STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
 | 
			
		||||
            ? amount
 | 
			
		||||
            : multiply(
 | 
			
		||||
                  amount,
 | 
			
		||||
                  transferRate(view, amount.get<MPTIssue>().getMptID()));
 | 
			
		||||
        actual += actualSend;
 | 
			
		||||
        takeFromSender += actualSend;
 | 
			
		||||
 | 
			
		||||
        JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID)
 | 
			
		||||
                        << " - > " << to_string(receiverID)
 | 
			
		||||
                        << " : deliver=" << amount.getFullText()
 | 
			
		||||
                        << " cost=" << actualSend.getFullText();
 | 
			
		||||
 | 
			
		||||
        if (auto const terResult =
 | 
			
		||||
                rippleCreditMPT(view, issuer, receiverID, amount, j))
 | 
			
		||||
            return terResult;
 | 
			
		||||
    }
 | 
			
		||||
    if (senderID != issuer && takeFromSender)
 | 
			
		||||
    {
 | 
			
		||||
        if (TER const terResult =
 | 
			
		||||
                rippleCreditMPT(view, senderID, issuer, takeFromSender, j))
 | 
			
		||||
            return terResult;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return tesSUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TER
 | 
			
		||||
accountSendMPT(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
@@ -2187,6 +2731,23 @@ accountSendMPT(
 | 
			
		||||
        view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TER
 | 
			
		||||
accountSendMultiMPT(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& senderID,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    MultiplePaymentDestinations const& receivers,
 | 
			
		||||
    beast::Journal j,
 | 
			
		||||
    WaiveTransferFee waiveFee)
 | 
			
		||||
{
 | 
			
		||||
    XRPL_ASSERT(asset.holds<MPTIssue>(), "ripple::accountSendMultiMPT : MPT");
 | 
			
		||||
 | 
			
		||||
    STAmount actual;
 | 
			
		||||
 | 
			
		||||
    return rippleSendMultiMPT(
 | 
			
		||||
        view, senderID, asset, receivers, actual, j, waiveFee);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TER
 | 
			
		||||
accountSend(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
@@ -2208,6 +2769,31 @@ accountSend(
 | 
			
		||||
        saAmount.asset().value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TER
 | 
			
		||||
accountSendMulti(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
    AccountID const& senderID,
 | 
			
		||||
    Asset const& asset,
 | 
			
		||||
    MultiplePaymentDestinations const& receivers,
 | 
			
		||||
    beast::Journal j,
 | 
			
		||||
    WaiveTransferFee waiveFee)
 | 
			
		||||
{
 | 
			
		||||
    XRPL_ASSERT_PARTS(
 | 
			
		||||
        receivers.size() > 1,
 | 
			
		||||
        "ripple::accountSendMulti",
 | 
			
		||||
        "multiple recipients provided");
 | 
			
		||||
    return std::visit(
 | 
			
		||||
        [&]<ValidIssueType TIss>(TIss const& issue) {
 | 
			
		||||
            if constexpr (std::is_same_v<TIss, Issue>)
 | 
			
		||||
                return accountSendMultiIOU(
 | 
			
		||||
                    view, senderID, asset, receivers, j, waiveFee);
 | 
			
		||||
            else
 | 
			
		||||
                return accountSendMultiMPT(
 | 
			
		||||
                    view, senderID, asset, receivers, j, waiveFee);
 | 
			
		||||
        },
 | 
			
		||||
        asset.value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
updateTrustLine(
 | 
			
		||||
    ApplyView& view,
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/detail/BatchWriter.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/BatchWriter.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -17,11 +17,10 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Database.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/chrono.h>
 | 
			
		||||
#include <xrpl/beast/core/CurrentThreadName.h>
 | 
			
		||||
#include <xrpl/json/json_value.h>
 | 
			
		||||
#include <xrpl/nodestore/Database.h>
 | 
			
		||||
#include <xrpl/protocol/HashPrefix.h>
 | 
			
		||||
#include <xrpl/protocol/jss.h>
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/detail/DatabaseNodeImp.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/DatabaseNodeImp.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/detail/DatabaseRotatingImp.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -17,10 +17,9 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/detail/DecodedBlob.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/safe_cast.h>
 | 
			
		||||
#include <xrpl/beast/utility/instrumentation.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/DummyScheduler.h>
 | 
			
		||||
#include <xrpl/nodestore/DummyScheduler.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -17,8 +17,8 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/detail/DatabaseNodeImp.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/ManagerImp.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/DatabaseNodeImp.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/ManagerImp.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string/predicate.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +41,27 @@ ManagerImp::missing_backend()
 | 
			
		||||
        "please see the rippled-example.cfg file!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// We shouldn't rely on global variables for lifetime management because their
 | 
			
		||||
// lifetime is not well-defined. ManagerImp may get destroyed before the Factory
 | 
			
		||||
// classes, and then, calling Manager::instance().erase() in the destructors of
 | 
			
		||||
// the Factory classes is an undefined behaviour.
 | 
			
		||||
void
 | 
			
		||||
registerNuDBFactory(Manager& manager);
 | 
			
		||||
void
 | 
			
		||||
registerRocksDBFactory(Manager& manager);
 | 
			
		||||
void
 | 
			
		||||
registerNullFactory(Manager& manager);
 | 
			
		||||
void
 | 
			
		||||
registerMemoryFactory(Manager& manager);
 | 
			
		||||
 | 
			
		||||
ManagerImp::ManagerImp()
 | 
			
		||||
{
 | 
			
		||||
    registerNuDBFactory(*this);
 | 
			
		||||
    registerRocksDBFactory(*this);
 | 
			
		||||
    registerNullFactory(*this);
 | 
			
		||||
    registerMemoryFactory(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Backend>
 | 
			
		||||
ManagerImp::make_Backend(
 | 
			
		||||
    Section const& parameters,
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/NodeObject.h>
 | 
			
		||||
#include <xrpl/nodestore/NodeObject.h>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
@@ -17,10 +17,9 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Factory.h>
 | 
			
		||||
#include <xrpld/nodestore/Manager.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/nodestore/Factory.h>
 | 
			
		||||
#include <xrpl/nodestore/Manager.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/beast/core/string.hpp>
 | 
			
		||||
#include <boost/core/ignore_unused.hpp>
 | 
			
		||||
@@ -46,10 +45,10 @@ class MemoryFactory : public Factory
 | 
			
		||||
private:
 | 
			
		||||
    std::mutex mutex_;
 | 
			
		||||
    std::map<std::string, MemoryDB, boost::beast::iless> map_;
 | 
			
		||||
    Manager& manager_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    MemoryFactory();
 | 
			
		||||
    ~MemoryFactory() override;
 | 
			
		||||
    explicit MemoryFactory(Manager& manager);
 | 
			
		||||
 | 
			
		||||
    std::string
 | 
			
		||||
    getName() const override;
 | 
			
		||||
@@ -75,7 +74,14 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static MemoryFactory memoryFactory;
 | 
			
		||||
MemoryFactory* memoryFactory = nullptr;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
registerMemoryFactory(Manager& manager)
 | 
			
		||||
{
 | 
			
		||||
    static MemoryFactory instance{manager};
 | 
			
		||||
    memoryFactory = &instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
@@ -112,9 +118,9 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    open(bool createIfMissing) override
 | 
			
		||||
    open(bool) override
 | 
			
		||||
    {
 | 
			
		||||
        db_ = &memoryFactory.open(name_);
 | 
			
		||||
        db_ = &memoryFactory->open(name_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool
 | 
			
		||||
@@ -219,14 +225,9 @@ public:
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
MemoryFactory::MemoryFactory()
 | 
			
		||||
MemoryFactory::MemoryFactory(Manager& manager) : manager_(manager)
 | 
			
		||||
{
 | 
			
		||||
    Manager::instance().insert(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemoryFactory::~MemoryFactory()
 | 
			
		||||
{
 | 
			
		||||
    Manager::instance().erase(*this);
 | 
			
		||||
    manager_.insert(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string
 | 
			
		||||
@@ -17,15 +17,14 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Factory.h>
 | 
			
		||||
#include <xrpld/nodestore/Manager.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/DecodedBlob.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/EncodedBlob.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/codec.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/beast/core/LexicalCast.h>
 | 
			
		||||
#include <xrpl/beast/utility/instrumentation.h>
 | 
			
		||||
#include <xrpl/nodestore/Factory.h>
 | 
			
		||||
#include <xrpl/nodestore/Manager.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/EncodedBlob.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/codec.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -427,15 +426,13 @@ private:
 | 
			
		||||
 | 
			
		||||
class NuDBFactory : public Factory
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    NuDBFactory()
 | 
			
		||||
    {
 | 
			
		||||
        Manager::instance().insert(*this);
 | 
			
		||||
    }
 | 
			
		||||
private:
 | 
			
		||||
    Manager& manager_;
 | 
			
		||||
 | 
			
		||||
    ~NuDBFactory() override
 | 
			
		||||
public:
 | 
			
		||||
    explicit NuDBFactory(Manager& manager) : manager_(manager)
 | 
			
		||||
    {
 | 
			
		||||
        Manager::instance().erase(*this);
 | 
			
		||||
        manager_.insert(*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string
 | 
			
		||||
@@ -470,7 +467,11 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static NuDBFactory nuDBFactory;
 | 
			
		||||
void
 | 
			
		||||
registerNuDBFactory(Manager& manager)
 | 
			
		||||
{
 | 
			
		||||
    static NuDBFactory instance{manager};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace NodeStore
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
@@ -17,8 +17,8 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/nodestore/Factory.h>
 | 
			
		||||
#include <xrpld/nodestore/Manager.h>
 | 
			
		||||
#include <xrpl/nodestore/Factory.h>
 | 
			
		||||
#include <xrpl/nodestore/Manager.h>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
@@ -111,15 +111,13 @@ private:
 | 
			
		||||
 | 
			
		||||
class NullFactory : public Factory
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    NullFactory()
 | 
			
		||||
    {
 | 
			
		||||
        Manager::instance().insert(*this);
 | 
			
		||||
    }
 | 
			
		||||
private:
 | 
			
		||||
    Manager& manager_;
 | 
			
		||||
 | 
			
		||||
    ~NullFactory() override
 | 
			
		||||
public:
 | 
			
		||||
    explicit NullFactory(Manager& manager) : manager_(manager)
 | 
			
		||||
    {
 | 
			
		||||
        Manager::instance().erase(*this);
 | 
			
		||||
        manager_.insert(*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string
 | 
			
		||||
@@ -140,7 +138,11 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static NullFactory nullFactory;
 | 
			
		||||
void
 | 
			
		||||
registerNullFactory(Manager& manager)
 | 
			
		||||
{
 | 
			
		||||
    static NullFactory instance{manager};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace NodeStore
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
@@ -17,20 +17,18 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/unity/rocksdb.h>
 | 
			
		||||
#include <xrpl/basics/rocksdb.h>
 | 
			
		||||
 | 
			
		||||
#if RIPPLE_ROCKSDB_AVAILABLE
 | 
			
		||||
#include <xrpld/core/Config.h>  // VFALCO Bad dependency
 | 
			
		||||
#include <xrpld/nodestore/Factory.h>
 | 
			
		||||
#include <xrpld/nodestore/Manager.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/BatchWriter.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/DecodedBlob.h>
 | 
			
		||||
#include <xrpld/nodestore/detail/EncodedBlob.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/ByteUtilities.h>
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/basics/safe_cast.h>
 | 
			
		||||
#include <xrpl/beast/core/CurrentThreadName.h>
 | 
			
		||||
#include <xrpl/nodestore/Factory.h>
 | 
			
		||||
#include <xrpl/nodestore/Manager.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/BatchWriter.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
 | 
			
		||||
#include <xrpl/nodestore/detail/EncodedBlob.h>
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <memory>
 | 
			
		||||
@@ -461,17 +459,15 @@ public:
 | 
			
		||||
 | 
			
		||||
class RocksDBFactory : public Factory
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    Manager& manager_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    RocksDBEnv m_env;
 | 
			
		||||
 | 
			
		||||
    RocksDBFactory()
 | 
			
		||||
    RocksDBFactory(Manager& manager) : manager_(manager)
 | 
			
		||||
    {
 | 
			
		||||
        Manager::instance().insert(*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~RocksDBFactory() override
 | 
			
		||||
    {
 | 
			
		||||
        Manager::instance().erase(*this);
 | 
			
		||||
        manager_.insert(*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string
 | 
			
		||||
@@ -493,7 +489,11 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static RocksDBFactory rocksDBFactory;
 | 
			
		||||
void
 | 
			
		||||
registerRocksDBFactory(Manager& manager)
 | 
			
		||||
{
 | 
			
		||||
    static RocksDBFactory instance{manager};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace NodeStore
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
@@ -96,6 +96,8 @@ enum class LedgerNameSpace : std::uint16_t {
 | 
			
		||||
    PERMISSIONED_DOMAIN = 'm',
 | 
			
		||||
    DELEGATE = 'E',
 | 
			
		||||
    VAULT = 'V',
 | 
			
		||||
    LOAN_BROKER = 'l',  // lower-case L
 | 
			
		||||
    LOAN = 'L',
 | 
			
		||||
 | 
			
		||||
    // No longer used or supported. Left here to reserve the space
 | 
			
		||||
    // to avoid accidental reuse.
 | 
			
		||||
@@ -566,6 +568,18 @@ vault(AccountID const& owner, std::uint32_t seq) noexcept
 | 
			
		||||
    return vault(indexHash(LedgerNameSpace::VAULT, owner, seq));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Keylet
 | 
			
		||||
loanbroker(AccountID const& owner, std::uint32_t seq) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return loanbroker(indexHash(LedgerNameSpace::LOAN_BROKER, owner, seq));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Keylet
 | 
			
		||||
loan(uint256 const& loanBrokerID, std::uint32_t loanSeq) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return loan(indexHash(LedgerNameSpace::LOAN, loanBrokerID, loanSeq));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Keylet
 | 
			
		||||
permissionedDomain(AccountID const& account, std::uint32_t seq) noexcept
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -172,6 +172,14 @@ InnerObjectFormats::InnerObjectFormats()
 | 
			
		||||
            {sfBookDirectory, soeREQUIRED},
 | 
			
		||||
            {sfBookNode, soeREQUIRED},
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    add(sfCounterpartySignature.jsonName,
 | 
			
		||||
        sfCounterpartySignature.getCode(),
 | 
			
		||||
        {
 | 
			
		||||
            {sfSigningPubKey, soeOPTIONAL},
 | 
			
		||||
            {sfTxnSignature, soeOPTIONAL},
 | 
			
		||||
            {sfSigners, soeOPTIONAL},
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
InnerObjectFormats const&
 | 
			
		||||
 
 | 
			
		||||
@@ -174,21 +174,22 @@ Permission::isDelegatable(
 | 
			
		||||
    auto const txType = permissionToTxType(permissionValue);
 | 
			
		||||
    auto const it = delegatableTx_.find(txType);
 | 
			
		||||
 | 
			
		||||
    if (rules.enabled(fixDelegateV1_1))
 | 
			
		||||
    {
 | 
			
		||||
        if (it == delegatableTx_.end())
 | 
			
		||||
            return false;
 | 
			
		||||
    if (it == delegatableTx_.end())
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
        auto const feature = getTxFeature(txType);
 | 
			
		||||
    auto const txFeaturesIt = txFeatureMap_.find(txType);
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
        txFeaturesIt != txFeatureMap_.end(),
 | 
			
		||||
        "ripple::Permissions::isDelegatable : tx exists in txFeatureMap_");
 | 
			
		||||
 | 
			
		||||
        // fixDelegateV1_1: Delegation is only allowed if the required amendment
 | 
			
		||||
        // for the transaction is enabled. For transactions that do not require
 | 
			
		||||
        // an amendment, delegation is always allowed.
 | 
			
		||||
        if (feature && !rules.enabled(*feature))
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
    // Delegation is only allowed if the required amendment for the transaction
 | 
			
		||||
    // is enabled. For transactions that do not require an amendment, delegation
 | 
			
		||||
    // is always allowed.
 | 
			
		||||
    if (txFeaturesIt->second != uint256{} &&
 | 
			
		||||
        !rules.enabled(txFeaturesIt->second))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (it != delegatableTx_.end() && it->second == Delegation::notDelegatable)
 | 
			
		||||
    if (it->second == Delegation::notDelegatable)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -131,17 +131,6 @@ Rules::enabled(uint256 const& feature) const
 | 
			
		||||
{
 | 
			
		||||
    XRPL_ASSERT(impl_, "ripple::Rules::enabled : initialized");
 | 
			
		||||
 | 
			
		||||
    // The functionality of the "NonFungibleTokensV1_1" amendment is
 | 
			
		||||
    // precisely the functionality of the following three amendments
 | 
			
		||||
    // so if their status is ever queried individually, we inject an
 | 
			
		||||
    // extra check here to simplify the checking elsewhere.
 | 
			
		||||
    if (feature == featureNonFungibleTokensV1 ||
 | 
			
		||||
        feature == fixNFTokenNegOffer || feature == fixNFTokenDirV1)
 | 
			
		||||
    {
 | 
			
		||||
        if (impl_->enabled(featureNonFungibleTokensV1_1))
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return impl_->enabled(feature);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -298,7 +298,7 @@ STAmount::xrp() const
 | 
			
		||||
IOUAmount
 | 
			
		||||
STAmount::iou() const
 | 
			
		||||
{
 | 
			
		||||
    if (native() || !holds<Issue>())
 | 
			
		||||
    if (integral())
 | 
			
		||||
        Throw<std::logic_error>("Cannot return non-IOU STAmount as IOUAmount");
 | 
			
		||||
 | 
			
		||||
    auto mantissa = static_cast<std::int64_t>(mValue);
 | 
			
		||||
@@ -1480,6 +1480,31 @@ canonicalizeRoundStrict(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
STAmount
 | 
			
		||||
roundToScale(STAmount value, std::int32_t scale, Number::rounding_mode rounding)
 | 
			
		||||
{
 | 
			
		||||
    // Nothing to do for intgral types.
 | 
			
		||||
    if (value.integral())
 | 
			
		||||
        return value;
 | 
			
		||||
 | 
			
		||||
    // If the value's exponent is greater than or equal to the scale, then
 | 
			
		||||
    // rounding will do nothing, and might even lose precision, so just return
 | 
			
		||||
    // the value.
 | 
			
		||||
    if (value.exponent() >= scale)
 | 
			
		||||
        return value;
 | 
			
		||||
 | 
			
		||||
    STAmount referenceValue{
 | 
			
		||||
        value.asset(), STAmount::cMinValue, scale, value.negative()};
 | 
			
		||||
 | 
			
		||||
    NumberRoundModeGuard mg(rounding);
 | 
			
		||||
    // With an IOU, the total will be truncated to the precision of the
 | 
			
		||||
    // larger value: referenceValue
 | 
			
		||||
    value += referenceValue;
 | 
			
		||||
    // Remove the reference value, and we're left with the rounded value.
 | 
			
		||||
    value -= referenceValue;
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
// We need a class that has an interface similar to NumberRoundModeGuard
 | 
			
		||||
 
 | 
			
		||||
@@ -286,7 +286,6 @@ STTx::checkSign(
 | 
			
		||||
    if (auto const ret = checkSign(requireCanonicalSig, rules, *this); !ret)
 | 
			
		||||
        return ret;
 | 
			
		||||
 | 
			
		||||
    /* Placeholder for field that will be added by Lending Protocol
 | 
			
		||||
    if (isFieldPresent(sfCounterpartySignature))
 | 
			
		||||
    {
 | 
			
		||||
        auto const counterSig = getFieldObject(sfCounterpartySignature);
 | 
			
		||||
@@ -294,7 +293,6 @@ STTx::checkSign(
 | 
			
		||||
            !ret)
 | 
			
		||||
            return Unexpected("Counterparty: " + ret.error());
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -127,7 +127,6 @@ transResults()
 | 
			
		||||
        MAKE_ERROR(tecLIMIT_EXCEEDED,                "Limit exceeded."),
 | 
			
		||||
        MAKE_ERROR(tecPSEUDO_ACCOUNT,                "This operation is not allowed against a pseudo-account."),
 | 
			
		||||
        MAKE_ERROR(tecPRECISION_LOSS,                "The amounts used by the transaction cannot interact."),
 | 
			
		||||
        MAKE_ERROR(tecNO_DELEGATE_PERMISSION,        "Delegated account lacks permission to perform this transaction."),
 | 
			
		||||
 | 
			
		||||
        MAKE_ERROR(tefALREADY,                     "The exact transaction was already in this ledger."),
 | 
			
		||||
        MAKE_ERROR(tefBAD_ADD_AUTH,                "Not authorized to add account."),
 | 
			
		||||
@@ -235,6 +234,7 @@ transResults()
 | 
			
		||||
        MAKE_ERROR(terPRE_TICKET,             "Ticket is not yet in ledger."),
 | 
			
		||||
        MAKE_ERROR(terNO_AMM,                 "AMM doesn't exist for the asset pair."),
 | 
			
		||||
        MAKE_ERROR(terADDRESS_COLLISION,      "Failed to allocate an unique account address."),
 | 
			
		||||
        MAKE_ERROR(terNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."),
 | 
			
		||||
 | 
			
		||||
        MAKE_ERROR(tesSUCCESS,                "The transaction was applied. Only final in a validated ledger."),
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -39,35 +39,13 @@
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
template <class T>
 | 
			
		||||
TxMeta::TxMeta(
 | 
			
		||||
    uint256 const& txid,
 | 
			
		||||
    std::uint32_t ledger,
 | 
			
		||||
    T const& data,
 | 
			
		||||
    CtorHelper)
 | 
			
		||||
    : mTransactionID(txid), mLedger(ledger), mNodes(sfAffectedNodes, 32)
 | 
			
		||||
{
 | 
			
		||||
    SerialIter sit(makeSlice(data));
 | 
			
		||||
 | 
			
		||||
    STObject obj(sit, sfMetadata);
 | 
			
		||||
    mResult = obj.getFieldU8(sfTransactionResult);
 | 
			
		||||
    mIndex = obj.getFieldU32(sfTransactionIndex);
 | 
			
		||||
    mNodes = *dynamic_cast<STArray*>(&obj.getField(sfAffectedNodes));
 | 
			
		||||
 | 
			
		||||
    if (obj.isFieldPresent(sfDeliveredAmount))
 | 
			
		||||
        setDeliveredAmount(obj.getFieldAmount(sfDeliveredAmount));
 | 
			
		||||
 | 
			
		||||
    if (obj.isFieldPresent(sfParentBatchID))
 | 
			
		||||
        setParentBatchId(obj.getFieldH256(sfParentBatchID));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj)
 | 
			
		||||
    : mTransactionID(txid)
 | 
			
		||||
    , mLedger(ledger)
 | 
			
		||||
    , mNodes(obj.getFieldArray(sfAffectedNodes))
 | 
			
		||||
    : transactionID_(txid)
 | 
			
		||||
    , ledgerSeq_(ledger)
 | 
			
		||||
    , nodes_(obj.getFieldArray(sfAffectedNodes))
 | 
			
		||||
{
 | 
			
		||||
    mResult = obj.getFieldU8(sfTransactionResult);
 | 
			
		||||
    mIndex = obj.getFieldU32(sfTransactionIndex);
 | 
			
		||||
    result_ = obj.getFieldU8(sfTransactionResult);
 | 
			
		||||
    index_ = obj.getFieldU32(sfTransactionIndex);
 | 
			
		||||
 | 
			
		||||
    auto affectedNodes =
 | 
			
		||||
        dynamic_cast<STArray const*>(obj.peekAtPField(sfAffectedNodes));
 | 
			
		||||
@@ -75,40 +53,32 @@ TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj)
 | 
			
		||||
        affectedNodes,
 | 
			
		||||
        "ripple::TxMeta::TxMeta(STObject) : type cast succeeded");
 | 
			
		||||
    if (affectedNodes)
 | 
			
		||||
        mNodes = *affectedNodes;
 | 
			
		||||
        nodes_ = *affectedNodes;
 | 
			
		||||
 | 
			
		||||
    if (obj.isFieldPresent(sfDeliveredAmount))
 | 
			
		||||
        setDeliveredAmount(obj.getFieldAmount(sfDeliveredAmount));
 | 
			
		||||
 | 
			
		||||
    if (obj.isFieldPresent(sfParentBatchID))
 | 
			
		||||
        setParentBatchId(obj.getFieldH256(sfParentBatchID));
 | 
			
		||||
    setAdditionalFields(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, Blob const& vec)
 | 
			
		||||
    : TxMeta(txid, ledger, vec, CtorHelper())
 | 
			
		||||
    : transactionID_(txid), ledgerSeq_(ledger), nodes_(sfAffectedNodes, 32)
 | 
			
		||||
{
 | 
			
		||||
    SerialIter sit(makeSlice(vec));
 | 
			
		||||
 | 
			
		||||
    STObject obj(sit, sfMetadata);
 | 
			
		||||
    result_ = obj.getFieldU8(sfTransactionResult);
 | 
			
		||||
    index_ = obj.getFieldU32(sfTransactionIndex);
 | 
			
		||||
    nodes_ = obj.getFieldArray(sfAffectedNodes);
 | 
			
		||||
 | 
			
		||||
    setAdditionalFields(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TxMeta::TxMeta(
 | 
			
		||||
    uint256 const& txid,
 | 
			
		||||
    std::uint32_t ledger,
 | 
			
		||||
    std::string const& data)
 | 
			
		||||
    : TxMeta(txid, ledger, data, CtorHelper())
 | 
			
		||||
TxMeta::TxMeta(uint256 const& transactionID, std::uint32_t ledger)
 | 
			
		||||
    : transactionID_(transactionID)
 | 
			
		||||
    , ledgerSeq_(ledger)
 | 
			
		||||
    , index_(std::numeric_limits<std::uint32_t>::max())
 | 
			
		||||
    , result_(255)
 | 
			
		||||
    , nodes_(sfAffectedNodes)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TxMeta::TxMeta(
 | 
			
		||||
    uint256 const& transactionID,
 | 
			
		||||
    std::uint32_t ledger,
 | 
			
		||||
    std::optional<uint256> parentBatchId)
 | 
			
		||||
    : mTransactionID(transactionID)
 | 
			
		||||
    , mLedger(ledger)
 | 
			
		||||
    , mIndex(static_cast<std::uint32_t>(-1))
 | 
			
		||||
    , mResult(255)
 | 
			
		||||
    , mParentBatchId(parentBatchId)
 | 
			
		||||
    , mNodes(sfAffectedNodes)
 | 
			
		||||
{
 | 
			
		||||
    mNodes.reserve(32);
 | 
			
		||||
    nodes_.reserve(32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -118,7 +88,7 @@ TxMeta::setAffectedNode(
 | 
			
		||||
    std::uint16_t nodeType)
 | 
			
		||||
{
 | 
			
		||||
    // make sure the node exists and force its type
 | 
			
		||||
    for (auto& n : mNodes)
 | 
			
		||||
    for (auto& n : nodes_)
 | 
			
		||||
    {
 | 
			
		||||
        if (n.getFieldH256(sfLedgerIndex) == node)
 | 
			
		||||
        {
 | 
			
		||||
@@ -128,8 +98,8 @@ TxMeta::setAffectedNode(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mNodes.push_back(STObject(type));
 | 
			
		||||
    STObject& obj = mNodes.back();
 | 
			
		||||
    nodes_.push_back(STObject(type));
 | 
			
		||||
    STObject& obj = nodes_.back();
 | 
			
		||||
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
        obj.getFName() == type,
 | 
			
		||||
@@ -146,14 +116,15 @@ TxMeta::getAffectedAccounts() const
 | 
			
		||||
 | 
			
		||||
    // This code should match the behavior of the JS method:
 | 
			
		||||
    // Meta#getAffectedAccounts
 | 
			
		||||
    for (auto const& it : mNodes)
 | 
			
		||||
    for (auto const& node : nodes_)
 | 
			
		||||
    {
 | 
			
		||||
        int index = it.getFieldIndex(
 | 
			
		||||
            (it.getFName() == sfCreatedNode) ? sfNewFields : sfFinalFields);
 | 
			
		||||
        int index = node.getFieldIndex(
 | 
			
		||||
            (node.getFName() == sfCreatedNode) ? sfNewFields : sfFinalFields);
 | 
			
		||||
 | 
			
		||||
        if (index != -1)
 | 
			
		||||
        {
 | 
			
		||||
            auto inner = dynamic_cast<STObject const*>(&it.peekAtIndex(index));
 | 
			
		||||
            auto const* inner =
 | 
			
		||||
                dynamic_cast<STObject const*>(&node.peekAtIndex(index));
 | 
			
		||||
            XRPL_ASSERT(
 | 
			
		||||
                inner,
 | 
			
		||||
                "ripple::getAffectedAccounts : STObject type cast succeeded");
 | 
			
		||||
@@ -213,13 +184,13 @@ STObject&
 | 
			
		||||
TxMeta::getAffectedNode(SLE::ref node, SField const& type)
 | 
			
		||||
{
 | 
			
		||||
    uint256 index = node->key();
 | 
			
		||||
    for (auto& n : mNodes)
 | 
			
		||||
    for (auto& n : nodes_)
 | 
			
		||||
    {
 | 
			
		||||
        if (n.getFieldH256(sfLedgerIndex) == index)
 | 
			
		||||
            return n;
 | 
			
		||||
    }
 | 
			
		||||
    mNodes.push_back(STObject(type));
 | 
			
		||||
    STObject& obj = mNodes.back();
 | 
			
		||||
    nodes_.push_back(STObject(type));
 | 
			
		||||
    STObject& obj = nodes_.back();
 | 
			
		||||
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
        obj.getFName() == type,
 | 
			
		||||
@@ -233,7 +204,7 @@ TxMeta::getAffectedNode(SLE::ref node, SField const& type)
 | 
			
		||||
STObject&
 | 
			
		||||
TxMeta::getAffectedNode(uint256 const& node)
 | 
			
		||||
{
 | 
			
		||||
    for (auto& n : mNodes)
 | 
			
		||||
    for (auto& n : nodes_)
 | 
			
		||||
    {
 | 
			
		||||
        if (n.getFieldH256(sfLedgerIndex) == node)
 | 
			
		||||
            return n;
 | 
			
		||||
@@ -241,7 +212,7 @@ TxMeta::getAffectedNode(uint256 const& node)
 | 
			
		||||
    // LCOV_EXCL_START
 | 
			
		||||
    UNREACHABLE("ripple::TxMeta::getAffectedNode(uint256) : node not found");
 | 
			
		||||
    Throw<std::runtime_error>("Affected node not found");
 | 
			
		||||
    return *(mNodes.begin());  // Silence compiler warning.
 | 
			
		||||
    return *(nodes_.begin());  // Silence compiler warning.
 | 
			
		||||
    // LCOV_EXCL_STOP
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -249,15 +220,15 @@ STObject
 | 
			
		||||
TxMeta::getAsObject() const
 | 
			
		||||
{
 | 
			
		||||
    STObject metaData(sfTransactionMetaData);
 | 
			
		||||
    XRPL_ASSERT(mResult != 255, "ripple::TxMeta::getAsObject : result is set");
 | 
			
		||||
    metaData.setFieldU8(sfTransactionResult, mResult);
 | 
			
		||||
    metaData.setFieldU32(sfTransactionIndex, mIndex);
 | 
			
		||||
    metaData.emplace_back(mNodes);
 | 
			
		||||
    if (hasDeliveredAmount())
 | 
			
		||||
        metaData.setFieldAmount(sfDeliveredAmount, getDeliveredAmount());
 | 
			
		||||
    XRPL_ASSERT(result_ != 255, "ripple::TxMeta::getAsObject : result_ is set");
 | 
			
		||||
    metaData.setFieldU8(sfTransactionResult, result_);
 | 
			
		||||
    metaData.setFieldU32(sfTransactionIndex, index_);
 | 
			
		||||
    metaData.emplace_back(nodes_);
 | 
			
		||||
    if (deliveredAmount_.has_value())
 | 
			
		||||
        metaData.setFieldAmount(sfDeliveredAmount, *deliveredAmount_);
 | 
			
		||||
 | 
			
		||||
    if (hasParentBatchId())
 | 
			
		||||
        metaData.setFieldH256(sfParentBatchID, getParentBatchId());
 | 
			
		||||
    if (parentBatchID_.has_value())
 | 
			
		||||
        metaData.setFieldH256(sfParentBatchID, *parentBatchID_);
 | 
			
		||||
 | 
			
		||||
    return metaData;
 | 
			
		||||
}
 | 
			
		||||
@@ -265,13 +236,13 @@ TxMeta::getAsObject() const
 | 
			
		||||
void
 | 
			
		||||
TxMeta::addRaw(Serializer& s, TER result, std::uint32_t index)
 | 
			
		||||
{
 | 
			
		||||
    mResult = TERtoInt(result);
 | 
			
		||||
    mIndex = index;
 | 
			
		||||
    result_ = TERtoInt(result);
 | 
			
		||||
    index_ = index;
 | 
			
		||||
    XRPL_ASSERT(
 | 
			
		||||
        (mResult == 0) || ((mResult > 100) && (mResult <= 255)),
 | 
			
		||||
        (result_ == 0) || ((result_ > 100) && (result_ <= 255)),
 | 
			
		||||
        "ripple::TxMeta::addRaw : valid TER input");
 | 
			
		||||
 | 
			
		||||
    mNodes.sort([](STObject const& o1, STObject const& o2) {
 | 
			
		||||
    nodes_.sort([](STObject const& o1, STObject const& o2) {
 | 
			
		||||
        return o1.getFieldH256(sfLedgerIndex) < o2.getFieldH256(sfLedgerIndex);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,14 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMap.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapAccountStateLeafNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapNodeID.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapSyncFilter.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTxLeafNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTxPlusMetaLeafNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/TaggedCache.ipp>
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMap.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapAccountStateLeafNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapNodeID.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapSyncFilter.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTxLeafNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -17,10 +17,9 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMap.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.ipp>
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMap.h>
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <stack>
 | 
			
		||||
@@ -17,16 +17,15 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
#include <xrpld/shamap/detail/TaggedPointer.ipp>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.ipp>
 | 
			
		||||
#include <xrpl/basics/Slice.h>
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/basics/spinlock.h>
 | 
			
		||||
#include <xrpl/protocol/HashPrefix.h>
 | 
			
		||||
#include <xrpl/protocol/digest.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
#include <xrpl/shamap/detail/TaggedPointer.ipp>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapLeafNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapLeafNode.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -17,12 +17,11 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMap.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapNodeID.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/beast/core/LexicalCast.h>
 | 
			
		||||
#include <xrpl/beast/utility/instrumentation.h>
 | 
			
		||||
#include <xrpl/protocol/Serializer.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMap.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapNodeID.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -17,11 +17,10 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMap.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapLeafNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapSyncFilter.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/random.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMap.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapLeafNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapSyncFilter.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -17,18 +17,17 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <xrpld/shamap/SHAMapAccountStateLeafNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTreeNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTxLeafNode.h>
 | 
			
		||||
#include <xrpld/shamap/SHAMapTxPlusMetaLeafNode.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpl/basics/IntrusivePointer.ipp>
 | 
			
		||||
#include <xrpl/basics/Slice.h>
 | 
			
		||||
#include <xrpl/basics/contract.h>
 | 
			
		||||
#include <xrpl/basics/safe_cast.h>
 | 
			
		||||
#include <xrpl/protocol/HashPrefix.h>
 | 
			
		||||
#include <xrpl/protocol/digest.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapAccountStateLeafNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapInnerNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTreeNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTxLeafNode.h>
 | 
			
		||||
#include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
 | 
			
		||||
@@ -545,8 +545,7 @@ public:
 | 
			
		||||
 | 
			
		||||
            for (auto const& [hash, nVotes] : votes)
 | 
			
		||||
            {
 | 
			
		||||
                if (rules.enabled(fixAmendmentMajorityCalc) ? nVotes >= i
 | 
			
		||||
                                                            : nVotes > i)
 | 
			
		||||
                if (nVotes >= i)
 | 
			
		||||
                {
 | 
			
		||||
                    // We vote yes on this amendment
 | 
			
		||||
                    field.push_back(hash);
 | 
			
		||||
@@ -982,10 +981,6 @@ public:
 | 
			
		||||
    void
 | 
			
		||||
    testChangedUNL(FeatureBitset const& feat)
 | 
			
		||||
    {
 | 
			
		||||
        // This test doesn't work without fixAmendmentMajorityCalc enabled.
 | 
			
		||||
        if (!feat[fixAmendmentMajorityCalc])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        testcase("changedUNL");
 | 
			
		||||
 | 
			
		||||
        auto const testAmendment = amendmentId("changedUNL");
 | 
			
		||||
@@ -1143,10 +1138,6 @@ public:
 | 
			
		||||
    void
 | 
			
		||||
    testValidatorFlapping(FeatureBitset const& feat)
 | 
			
		||||
    {
 | 
			
		||||
        // This test doesn't work without fixAmendmentMajorityCalc enabled.
 | 
			
		||||
        if (!feat[fixAmendmentMajorityCalc])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        testcase("validatorFlapping");
 | 
			
		||||
 | 
			
		||||
        // We run a test where a validator flaps on and off every 23 hours
 | 
			
		||||
@@ -1289,14 +1280,12 @@ public:
 | 
			
		||||
    run() override
 | 
			
		||||
    {
 | 
			
		||||
        FeatureBitset const all{test::jtx::testable_amendments()};
 | 
			
		||||
        FeatureBitset const fixMajorityCalc{fixAmendmentMajorityCalc};
 | 
			
		||||
 | 
			
		||||
        testConstruct();
 | 
			
		||||
        testGet();
 | 
			
		||||
        testBadConfig();
 | 
			
		||||
        testEnableVeto();
 | 
			
		||||
        testHasUnsupported();
 | 
			
		||||
        testFeature(all - fixMajorityCalc);
 | 
			
		||||
        testFeature(all);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
#include <test/jtx/utility.h>
 | 
			
		||||
 | 
			
		||||
#include <xrpld/app/misc/HashRouter.h>
 | 
			
		||||
#include <xrpld/app/misc/NetworkOPs.h>
 | 
			
		||||
#include <xrpld/app/misc/Transaction.h>
 | 
			
		||||
#include <xrpld/app/tx/apply.h>
 | 
			
		||||
#include <xrpld/app/tx/detail/Batch.h>
 | 
			
		||||
@@ -2313,9 +2314,12 @@ class Batch_test : public beast::unit_test::suite
 | 
			
		||||
            Serializer s;
 | 
			
		||||
            parsed.object->add(s);
 | 
			
		||||
            auto const jrr = env.rpc("submit", strHex(s.slice()))[jss::result];
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                jrr[jss::status] == "success" &&
 | 
			
		||||
                jrr[jss::engine_result] == "temINVALID_FLAG");
 | 
			
		||||
            BEAST_EXPECTS(
 | 
			
		||||
                jrr[jss::status] == "error" &&
 | 
			
		||||
                    jrr[jss::error] == "invalidTransaction" &&
 | 
			
		||||
                    jrr[jss::error_exception] ==
 | 
			
		||||
                        "fails local checks: Empty SigningPubKey.",
 | 
			
		||||
                to_string(jrr));
 | 
			
		||||
 | 
			
		||||
            env.close();
 | 
			
		||||
        }
 | 
			
		||||
@@ -2553,6 +2557,196 @@ class Batch_test : public beast::unit_test::suite
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testLoan(FeatureBitset features)
 | 
			
		||||
    {
 | 
			
		||||
        testcase("loan");
 | 
			
		||||
 | 
			
		||||
        using namespace test::jtx;
 | 
			
		||||
 | 
			
		||||
        test::jtx::Env env{
 | 
			
		||||
            *this,
 | 
			
		||||
            envconfig(),
 | 
			
		||||
            features | featureSingleAssetVault | featureLendingProtocol |
 | 
			
		||||
                featureMPTokensV1};
 | 
			
		||||
 | 
			
		||||
        Account const issuer{"issuer"};
 | 
			
		||||
        // For simplicity, lender will be the sole actor for the vault &
 | 
			
		||||
        // brokers.
 | 
			
		||||
        Account const lender{"lender"};
 | 
			
		||||
        // Borrower only wants to borrow
 | 
			
		||||
        Account const borrower{"borrower"};
 | 
			
		||||
 | 
			
		||||
        // Fund the accounts and trust lines with the same amount so that tests
 | 
			
		||||
        // can use the same values regardless of the asset.
 | 
			
		||||
        env.fund(XRP(100'000), issuer, noripple(lender, borrower));
 | 
			
		||||
        env.close();
 | 
			
		||||
 | 
			
		||||
        // Just use an XRP asset
 | 
			
		||||
        PrettyAsset const asset{xrpIssue(), 1'000'000};
 | 
			
		||||
 | 
			
		||||
        Vault vault{env};
 | 
			
		||||
 | 
			
		||||
        auto const deposit = asset(50'000);
 | 
			
		||||
        auto const debtMaximumValue = asset(25'000).value();
 | 
			
		||||
        auto const coverDepositValue = asset(1000).value();
 | 
			
		||||
 | 
			
		||||
        auto [tx, vaultKeylet] =
 | 
			
		||||
            vault.create({.owner = lender, .asset = asset});
 | 
			
		||||
        env(tx);
 | 
			
		||||
        env.close();
 | 
			
		||||
        BEAST_EXPECT(env.le(vaultKeylet));
 | 
			
		||||
 | 
			
		||||
        env(vault.deposit(
 | 
			
		||||
            {.depositor = lender, .id = vaultKeylet.key, .amount = deposit}));
 | 
			
		||||
        env.close();
 | 
			
		||||
 | 
			
		||||
        auto const brokerKeylet =
 | 
			
		||||
            keylet::loanbroker(lender.id(), env.seq(lender));
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            using namespace loanBroker;
 | 
			
		||||
            env(set(lender, vaultKeylet.key),
 | 
			
		||||
                managementFeeRate(TenthBips16(100)),
 | 
			
		||||
                debtMaximum(debtMaximumValue),
 | 
			
		||||
                coverRateMinimum(TenthBips32(percentageToTenthBips(10))),
 | 
			
		||||
                coverRateLiquidation(TenthBips32(percentageToTenthBips(25))));
 | 
			
		||||
 | 
			
		||||
            env(coverDeposit(lender, brokerKeylet.key, coverDepositValue));
 | 
			
		||||
 | 
			
		||||
            env.close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            using namespace loan;
 | 
			
		||||
            using namespace std::chrono_literals;
 | 
			
		||||
 | 
			
		||||
            auto const lenderSeq = env.seq(lender);
 | 
			
		||||
            auto const batchFee = batch::calcBatchFee(env, 0, 2);
 | 
			
		||||
 | 
			
		||||
            auto const loanKeylet = keylet::loan(brokerKeylet.key, 1);
 | 
			
		||||
            {
 | 
			
		||||
                auto const [txIDs, batchID] = submitBatch(
 | 
			
		||||
                    env,
 | 
			
		||||
                    temBAD_SIGNATURE,
 | 
			
		||||
                    batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        env.json(
 | 
			
		||||
                            set(lender, brokerKeylet.key, asset(1000).value()),
 | 
			
		||||
                            // Not allowed to include the counterparty signature
 | 
			
		||||
                            sig(sfCounterpartySignature, borrower),
 | 
			
		||||
                            sig(none),
 | 
			
		||||
                            fee(none),
 | 
			
		||||
                            seq(none)),
 | 
			
		||||
                        lenderSeq + 1),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        pay(lender,
 | 
			
		||||
                            loanKeylet.key,
 | 
			
		||||
                            STAmount{asset, asset(500).value()}),
 | 
			
		||||
                        lenderSeq + 2));
 | 
			
		||||
            }
 | 
			
		||||
            {
 | 
			
		||||
                auto const [txIDs, batchID] = submitBatch(
 | 
			
		||||
                    env,
 | 
			
		||||
                    temINVALID_INNER_BATCH,
 | 
			
		||||
                    batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        env.json(
 | 
			
		||||
                            set(lender, brokerKeylet.key, asset(1000).value()),
 | 
			
		||||
                            // Counterparty must be set
 | 
			
		||||
                            sig(none),
 | 
			
		||||
                            fee(none),
 | 
			
		||||
                            seq(none)),
 | 
			
		||||
                        lenderSeq + 1),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        pay(lender,
 | 
			
		||||
                            loanKeylet.key,
 | 
			
		||||
                            STAmount{asset, asset(500).value()}),
 | 
			
		||||
                        lenderSeq + 2));
 | 
			
		||||
            }
 | 
			
		||||
            {
 | 
			
		||||
                auto const [txIDs, batchID] = submitBatch(
 | 
			
		||||
                    env,
 | 
			
		||||
                    temBAD_SIGNER,
 | 
			
		||||
                    batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        env.json(
 | 
			
		||||
                            set(lender, brokerKeylet.key, asset(1000).value()),
 | 
			
		||||
                            // Counterparty must sign the outer transaction
 | 
			
		||||
                            counterparty(borrower.id()),
 | 
			
		||||
                            sig(none),
 | 
			
		||||
                            fee(none),
 | 
			
		||||
                            seq(none)),
 | 
			
		||||
                        lenderSeq + 1),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        pay(lender,
 | 
			
		||||
                            loanKeylet.key,
 | 
			
		||||
                            STAmount{asset, asset(500).value()}),
 | 
			
		||||
                        lenderSeq + 2));
 | 
			
		||||
            }
 | 
			
		||||
            {
 | 
			
		||||
                // LoanSet normally charges at least 2x base fee, but since the
 | 
			
		||||
                // signature check is done by the batch, it only charges the
 | 
			
		||||
                // base fee.
 | 
			
		||||
                auto const batchFee = batch::calcBatchFee(env, 1, 2);
 | 
			
		||||
                auto const [txIDs, batchID] = submitBatch(
 | 
			
		||||
                    env,
 | 
			
		||||
                    tesSUCCESS,
 | 
			
		||||
                    batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        env.json(
 | 
			
		||||
                            set(lender, brokerKeylet.key, asset(1000).value()),
 | 
			
		||||
                            counterparty(borrower.id()),
 | 
			
		||||
                            sig(none),
 | 
			
		||||
                            fee(none),
 | 
			
		||||
                            seq(none)),
 | 
			
		||||
                        lenderSeq + 1),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        pay(
 | 
			
		||||
                            // However, this inner transaction will fail,
 | 
			
		||||
                            // because the lender is not allowed to draw the
 | 
			
		||||
                            // transaction
 | 
			
		||||
                            lender,
 | 
			
		||||
                            loanKeylet.key,
 | 
			
		||||
                            STAmount{asset, asset(500).value()}),
 | 
			
		||||
                        lenderSeq + 2),
 | 
			
		||||
                    batch::sig(borrower));
 | 
			
		||||
            }
 | 
			
		||||
            env.close();
 | 
			
		||||
            BEAST_EXPECT(env.le(brokerKeylet));
 | 
			
		||||
            BEAST_EXPECT(!env.le(loanKeylet));
 | 
			
		||||
            {
 | 
			
		||||
                // LoanSet normally charges at least 2x base fee, but since the
 | 
			
		||||
                // signature check is done by the batch, it only charges the
 | 
			
		||||
                // base fee.
 | 
			
		||||
                auto const lenderSeq = env.seq(lender);
 | 
			
		||||
                auto const batchFee = batch::calcBatchFee(env, 1, 2);
 | 
			
		||||
                auto const [txIDs, batchID] = submitBatch(
 | 
			
		||||
                    env,
 | 
			
		||||
                    tesSUCCESS,
 | 
			
		||||
                    batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        env.json(
 | 
			
		||||
                            set(lender, brokerKeylet.key, asset(1000).value()),
 | 
			
		||||
                            counterparty(borrower.id()),
 | 
			
		||||
                            sig(none),
 | 
			
		||||
                            fee(none),
 | 
			
		||||
                            seq(none)),
 | 
			
		||||
                        lenderSeq + 1),
 | 
			
		||||
                    batch::inner(
 | 
			
		||||
                        manage(lender, loanKeylet.key, tfLoanImpair),
 | 
			
		||||
                        lenderSeq + 2),
 | 
			
		||||
                    batch::sig(borrower));
 | 
			
		||||
            }
 | 
			
		||||
            env.close();
 | 
			
		||||
            BEAST_EXPECT(env.le(brokerKeylet));
 | 
			
		||||
            if (auto const sleLoan = env.le(loanKeylet); BEAST_EXPECT(sleLoan))
 | 
			
		||||
            {
 | 
			
		||||
                BEAST_EXPECT(sleLoan->isFlag(lsfLoanImpaired));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testObjectCreateSequence(FeatureBitset features)
 | 
			
		||||
    {
 | 
			
		||||
@@ -3946,14 +4140,13 @@ class Batch_test : public beast::unit_test::suite
 | 
			
		||||
                tesSUCCESS,
 | 
			
		||||
                batch::outer(gw, seq, batchFee, tfIndependent),
 | 
			
		||||
                batch::inner(jv1, seq + 1),
 | 
			
		||||
                // tecNO_DELEGATE_PERMISSION: not authorized to clear freeze
 | 
			
		||||
                // terNO_DELEGATE_PERMISSION: not authorized to clear freeze
 | 
			
		||||
                batch::inner(jv2, seq + 2));
 | 
			
		||||
            env.close();
 | 
			
		||||
 | 
			
		||||
            std::vector<TestLedgerData> testCases = {
 | 
			
		||||
                {0, "Batch", "tesSUCCESS", batchID, std::nullopt},
 | 
			
		||||
                {1, "TrustSet", "tesSUCCESS", txIDs[0], batchID},
 | 
			
		||||
                {2, "TrustSet", "tecNO_DELEGATE_PERMISSION", txIDs[1], batchID},
 | 
			
		||||
            };
 | 
			
		||||
            validateClosedLedger(env, testCases);
 | 
			
		||||
        }
 | 
			
		||||
@@ -4147,6 +4340,7 @@ class Batch_test : public beast::unit_test::suite
 | 
			
		||||
        testAccountActivation(features);
 | 
			
		||||
        testAccountSet(features);
 | 
			
		||||
        testAccountDelete(features);
 | 
			
		||||
        testLoan(features);
 | 
			
		||||
        testObjectCreateSequence(features);
 | 
			
		||||
        testObjectCreateTicket(features);
 | 
			
		||||
        testObjectCreate3rdParty(features);
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user