import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'dart:convert'; import 'dart:math' as math; import 'package:crypto/crypto.dart'; class DrawVerificationWidget extends StatefulWidget { final String serverSeed; final String serverSeedHash; final String clientSeed; final int nonce; final List> eligibleMembers; final String winnerId; const DrawVerificationWidget({ super.key, required this.serverSeed, required this.serverSeedHash, required this.clientSeed, required this.nonce, required this.eligibleMembers, required this.winnerId, }); @override State createState() => _DrawVerificationWidgetState(); } class _DrawVerificationWidgetState extends State { bool _isVerifying = false; bool _isVerified = false; String? _calculatedWinnerId; String? _resultHash; String? _verificationError; @override void initState() { super.initState(); _verifyDraw(); } void _verifyDraw() async { setState(() { _isVerifying = true; _verificationError = null; }); try { // Step 1: Verify server seed hash final calculatedServerSeedHash = _generateServerSeedHash(widget.serverSeed); if (calculatedServerSeedHash != widget.serverSeedHash) { throw Exception('Server seed hash verification failed'); } // Step 2: Generate result hash _resultHash = _generateResultHash(); // Step 3: Calculate winner _calculatedWinnerId = _calculateWinner(); // Step 4: Verify winner matches if (_calculatedWinnerId == widget.winnerId) { setState(() { _isVerified = true; _isVerifying = false; }); } else { throw Exception('Winner verification failed'); } } catch (e) { setState(() { _verificationError = e.toString(); _isVerifying = false; }); } } String _generateServerSeedHash(String serverSeed) { final bytes = utf8.encode(serverSeed); final digest = sha256.convert(bytes); return digest.toString(); } String _generateResultHash() { final combinedSeed = '${widget.serverSeed}:${widget.clientSeed}:${widget.nonce}'; final bytes = utf8.encode(combinedSeed); final digest = sha256.convert(bytes); return digest.toString(); } String _calculateWinner() { final combinedSeed = '${widget.serverSeed}:${widget.clientSeed}:${widget.nonce}'; final hash = _generateHash(combinedSeed); final randomValue = _hashToNumber(hash); final selectedIndex = randomValue % widget.eligibleMembers.length; return widget.eligibleMembers[selectedIndex]['id']; } String _generateHash(String input) { int hash = 0; for (int i = 0; i < input.length; i++) { hash = ((hash << 5) - hash + input.codeUnitAt(i)) & 0xffffffff; } return hash.abs().toString(); } int _hashToNumber(String hash) { return int.parse(hash.substring(0, math.min(10, hash.length))); } @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(20.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), border: Border.all( color: _isVerified ? Colors.green.shade300 : _verificationError != null ? Colors.red.shade300 : Colors.grey.shade300, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10.r, offset: Offset(0, 2.h), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row( children: [ Icon( _isVerifying ? Icons.hourglass_empty : _isVerified ? Icons.verified : Icons.error, color: _isVerifying ? Colors.orange.shade600 : _isVerified ? Colors.green.shade600 : Colors.red.shade600, size: 24.w, ), SizedBox(width: 12.w), Text( 'Draw Verification', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), const Spacer(), if (_isVerifying) SizedBox( width: 20.w, height: 20.w, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.orange.shade600), ), ), ], ), SizedBox(height: 16.h), // Verification Status Container( padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: _isVerifying ? Colors.orange.shade50 : _isVerified ? Colors.green.shade50 : Colors.red.shade50, borderRadius: BorderRadius.circular(8.r), border: Border.all( color: _isVerifying ? Colors.orange.shade200 : _isVerified ? Colors.green.shade200 : Colors.red.shade200, ), ), child: Row( children: [ Icon( _isVerifying ? Icons.hourglass_empty : _isVerified ? Icons.check_circle : Icons.cancel, color: _isVerifying ? Colors.orange.shade600 : _isVerified ? Colors.green.shade600 : Colors.red.shade600, size: 20.w, ), SizedBox(width: 12.w), Expanded( child: Text( _isVerifying ? 'Verifying draw fairness...' : _isVerified ? 'Draw verified as fair and transparent' : 'Verification failed: $_verificationError', style: TextStyle( fontSize: 14.sp, color: _isVerifying ? Colors.orange.shade700 : _isVerified ? Colors.green.shade700 : Colors.red.shade700, fontWeight: FontWeight.w500, ), ), ), ], ), ), SizedBox(height: 16.h), // Verification Details if (_isVerified || _verificationError != null) ...[ Text( 'Verification Details', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 12.h), _buildVerificationDetail('Server Seed Hash', widget.serverSeedHash), _buildVerificationDetail('Client Seed', widget.clientSeed), _buildVerificationDetail('Nonce', widget.nonce.toString()), if (_resultHash != null) _buildVerificationDetail('Result Hash', _resultHash!), if (_calculatedWinnerId != null) _buildVerificationDetail('Calculated Winner ID', _calculatedWinnerId!), _buildVerificationDetail('Declared Winner ID', widget.winnerId), _buildVerificationDetail('Eligible Members', '${widget.eligibleMembers.length}'), ], // Verification Steps if (_isVerified) ...[ SizedBox(height: 16.h), Text( 'Verification Steps', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 12.h), _buildVerificationStep( '1', 'Server Seed Hash Verified', 'The provided server seed matches its hash', true, ), _buildVerificationStep( '2', 'Result Hash Generated', 'Combined seeds and nonce to create result hash', true, ), _buildVerificationStep( '3', 'Winner Calculated', 'Used result hash to determine winner index', true, ), _buildVerificationStep( '4', 'Winner Verified', 'Calculated winner matches declared winner', true, ), ], // Retry Button if (_verificationError != null) ...[ SizedBox(height: 16.h), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _verifyDraw, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade600, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 12.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), ), child: Text( 'Retry Verification', style: TextStyle(fontSize: 14.sp), ), ), ), ], ], ), ); } Widget _buildVerificationDetail(String label, String value) { return Container( margin: EdgeInsets.only(bottom: 8.h), padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(6.r), border: Border.all(color: Colors.grey.shade200), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, child: Text( label, style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, fontWeight: FontWeight.w500, ), ), ), Expanded( flex: 3, child: Text( value, style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade800, fontFamily: 'monospace', ), ), ), ], ), ); } Widget _buildVerificationStep(String step, String title, String description, bool isSuccess) { return Container( margin: EdgeInsets.only(bottom: 8.h), padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: isSuccess ? Colors.green.shade50 : Colors.red.shade50, borderRadius: BorderRadius.circular(6.r), border: Border.all( color: isSuccess ? Colors.green.shade200 : Colors.red.shade200, ), ), child: Row( children: [ Container( width: 24.w, height: 24.w, decoration: BoxDecoration( color: isSuccess ? Colors.green.shade100 : Colors.red.shade100, shape: BoxShape.circle, ), child: Center( child: Text( step, style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w600, color: isSuccess ? Colors.green.shade700 : Colors.red.shade700, ), ), ), ), SizedBox(width: 12.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: isSuccess ? Colors.green.shade800 : Colors.red.shade800, ), ), Text( description, style: TextStyle( fontSize: 12.sp, color: isSuccess ? Colors.green.shade700 : Colors.red.shade700, ), ), ], ), ), Icon( isSuccess ? Icons.check_circle : Icons.cancel, color: isSuccess ? Colors.green.shade600 : Colors.red.shade600, size: 20.w, ), ], ), ); } }