red-team send-path hardening: hold pada verify False/None, _send_rejected whitelist, fee guard, non-dict skip (V61/B20-B23)
This commit is contained in:
1 parent
69704fa23d
commit
d6b5927307
8 files changed
+182
-32
No files matched your search
+63
-2
@@ -40,7 +40,10 @@ class FakeSigned:
|
||||
if self._fails > 0:
|
||||
self._fails -= 1
|
||||
raise RuntimeError('broadcast timeout (mock)')
|
||||
return {'processed': True}
|
||||
# V61: bentuk sukses nodeos asli (whitelist `_send_rejected` butuh
|
||||
# `transaction_id` + receipt `executed`).
|
||||
return {'transaction_id': self._txid,
|
||||
'processed': {'receipt': {'status': 'executed'}}}
|
||||
|
||||
|
||||
def make_voters_db(path, rows):
|
||||
@@ -313,6 +316,7 @@ def main():
|
||||
fresh),
|
||||
('bbb2', '1.0', 700.0, '2026-08-05T00:00:00',
|
||||
fresh)])
|
||||
dist.time.sleep = lambda s: None # V61 `_verify_settled` tidur — mock
|
||||
dist.fetch_balance = lambda: Decimal('100.0000')
|
||||
dist.BP_PRIVATE_KEY = '5Ktest'
|
||||
dist.DISTRIBUTE_MAX_ATTEMPTS = 3
|
||||
@@ -361,6 +365,7 @@ def main():
|
||||
calls['run_updates'] == [(7, 'ok', 100.0)])
|
||||
|
||||
# ————— V22: guard anti-duplikat — timeout lalu terverifikasi mendarat —————
|
||||
_real_verify = dist.verify_txid # sandi utk oracle non-dict V61
|
||||
dist.build_signed_transfer = lambda to, amount, memo: FakeSigned('txL' + to, fails=1)
|
||||
dist.verify_txid = lambda txid: True # tx sebenarnya mendarat
|
||||
calls['updates'] = []
|
||||
@@ -412,7 +417,7 @@ def main():
|
||||
dist.DISTRIBUTE_MAX_ATTEMPTS = 1
|
||||
dist.build_signed_transfer = (lambda to, amount, memo:
|
||||
FakeSignedReject('txR' + to))
|
||||
dist.verify_txid = lambda txid: False # tak mendarat → boleh retry
|
||||
dist.verify_txid = lambda txid: False # tak mendarat → tetep ditahan (V61)
|
||||
calls['updates'] = []
|
||||
calls['run_updates'] = []
|
||||
with redirect_stdout(io.StringIO()):
|
||||
@@ -424,6 +429,62 @@ def main():
|
||||
check('V59 run status partial (rejection)',
|
||||
calls['run_updates'][0][1] == 'partial')
|
||||
|
||||
# ————— V61: verify_txid False (Hyperion bilang tak mendarat) → TAHAN —————
|
||||
# False bisa lag indeks utk tx yang BARU mendarat (pola V50/B14 di klaim):
|
||||
# resend = tapos baru = txid baru = ganda. → hold di attempt 1, ⊖ resend.
|
||||
dist.DISTRIBUTE_MAX_ATTEMPTS = 3 # default; hold harus jalan di attempt 1
|
||||
dist.time.sleep = lambda s: None
|
||||
sends_false = {'n': 0}
|
||||
|
||||
def counting_sign_false(to, amount, memo):
|
||||
sends_false['n'] += 1
|
||||
return FakeSigned('txF' + to, fails=1) # broadcast timeout (mungkin mendarat)
|
||||
|
||||
dist.build_signed_transfer = counting_sign_false
|
||||
dist.verify_txid = lambda txid: False # Hyperion: executed:false
|
||||
calls['updates'] = []
|
||||
calls['run_updates'] = []
|
||||
with redirect_stdout(io.StringIO()):
|
||||
dist.run_distribution(dry_run=False)
|
||||
check('V61 verify False → ditahan (failed), ⊖ resend',
|
||||
all(u[1] == 'failed' for u in calls['updates']))
|
||||
check('V61 hold attempt-1 → build sekali per pemilih (⊥ resend)',
|
||||
sends_false['n'] == 2)
|
||||
check('V61 run status partial',
|
||||
calls['run_updates'][0][1] == 'partial')
|
||||
|
||||
# ————— V61: `_send_rejected` whitelist — bentuk tak dikenal = tolak —————
|
||||
check('V61 sukses nodeos (transaction_id + executed) → ⊖ tolak',
|
||||
dist._send_rejected({'transaction_id': 'tx1',
|
||||
'processed': {'receipt': {'status': 'executed'}}})
|
||||
is False)
|
||||
check('V61 rejection 500 ber-error → tolak',
|
||||
dist._send_rejected({'code': 500, 'error': {'what': 'duplicate'}})
|
||||
is True)
|
||||
check('V61 rejection 500 TANPA error (bentuk proxy) → tolak',
|
||||
dist._send_rejected({'code': 500, 'message': 'Internal Error'}) is True)
|
||||
check('V61 soft_fail receipt → tolak',
|
||||
dist._send_rejected({'transaction_id': 'tx1',
|
||||
'processed': {'receipt': {'status': 'soft_fail'}}})
|
||||
is True)
|
||||
check('V61 non-dict response → tolak', dist._send_rejected(['x']) is True)
|
||||
|
||||
# ————— V61: `_get_json`/`verify_txid` ⊖ crash pada bentuk non-dict —————
|
||||
_orig_verify = dist.verify_txid # restore dr mock False di uji V61-F2
|
||||
dist.verify_txid = _real_verify
|
||||
dist.time.sleep = lambda s: None
|
||||
dist.HYPERION_NODES = ['http://hyperion-list']
|
||||
dist.requests.get = lambda url, params=None, timeout=10: (
|
||||
fake_post(['bukan', 'dict'])) # 200 tapi list (proxy salah)
|
||||
check('V61 _get_json non-dict 200 → None (⊥ crash)',
|
||||
dist._get_json('/v2/history/get_transaction') is None)
|
||||
check('V61 verify_txid non-dict → None',
|
||||
dist.verify_txid('tx1') is None)
|
||||
dist.HYPERION_NODES = _orig_hynodes
|
||||
dist.requests.get = _orig_get
|
||||
dist.verify_txid = _orig_verify
|
||||
dist.time.sleep = _orig_sleep
|
||||
|
||||
# ————— V35: kill-switch DISTRIBUTE_ENABLED=false → no-op —————
|
||||
make_voters_db(db.DB_PATH, [('aaa1', '1.0', 1200.0, '2026-08-05T00:00:00',
|
||||
fresh)])
|
||||
|
||||
Reference in new issue
Block a user