mirror of
				https://github.com/3b1b/manim.git
				synced 2025-10-31 16:08:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			5266 lines
		
	
	
	
		
			162 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			5266 lines
		
	
	
	
		
			162 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from big_ol_pile_of_manim_imports import *
 | |
| 
 | |
| from hashlib import sha256
 | |
| import binascii
 | |
| 
 | |
| #force_skipping
 | |
| #revert_to_original_skipping_status
 | |
| 
 | |
| BITCOIN_COLOR = "#f7931a"
 | |
| 
 | |
| def get_cursive_name(name):
 | |
|     result = TextMobject("\\normalfont\\calligra %s"%name)
 | |
|     result.set_stroke(width = 0.5)
 | |
|     return result
 | |
| 
 | |
| def sha256_bit_string(message):
 | |
|     hexdigest = sha256(message).hexdigest()
 | |
|     return bin(int(hexdigest, 16))[2:]
 | |
| 
 | |
| def bit_string_to_mobject(bit_string):
 | |
|     line = TexMobject("0"*32)
 | |
|     pre_result = VGroup(*[
 | |
|         line.copy() for row in range(8)
 | |
|     ])
 | |
|     pre_result.arrange_submobjects(DOWN, buff = SMALL_BUFF)
 | |
|     result = VGroup(*it.chain(*pre_result))
 | |
|     result.scale(0.7)
 | |
|     bit_string = (256 - len(bit_string))*"0" + bit_string
 | |
| 
 | |
|     for i, (bit, part) in enumerate(zip(bit_string, result)):
 | |
|         if bit == "1":
 | |
|             one = TexMobject("1")[0]
 | |
|             one.replace(part, dim_to_match = 1)
 | |
|             result.submobjects[i] = one
 | |
| 
 | |
|     return result
 | |
| 
 | |
| def sha256_tex_mob(message, n_forced_start_zeros = 0):
 | |
|     true_bit_string = sha256_bit_string(message)
 | |
|     n = n_forced_start_zeros
 | |
|     bit_string = "0"*n + true_bit_string[n:]
 | |
|     return bit_string_to_mobject(bit_string)
 | |
| 
 | |
| class EthereumLogo(SVGMobject):
 | |
|     CONFIG = {
 | |
|         "file_name" : "ethereum_logo",
 | |
|         "stroke_width" : 0,
 | |
|         "fill_opacity" : 1,
 | |
|         "color_chars" : "8B8B48",
 | |
|         "height" : 0.5,
 | |
|     }
 | |
|     def __init__(self, **kwargs):
 | |
|         SVGMobject.__init__(self, **kwargs)
 | |
|         for part, char in zip(self.submobjects, self.color_chars):
 | |
|             part.set_color("#" + 6*char)
 | |
| 
 | |
| class LitecoinLogo(SVGMobject):
 | |
|     CONFIG = {
 | |
|         "file_name" : "litecoin_logo",
 | |
|         "stroke_width" : 0,
 | |
|         "fill_opacity" : 1,
 | |
|         "fill_color" : LIGHT_GREY,
 | |
|         "height" : 0.5,
 | |
|     }
 | |
| 
 | |
| class TenDollarBill(VGroup):
 | |
|     CONFIG = {
 | |
|         "color" : GREEN,
 | |
|         "height" : 0.5,
 | |
|         "mark_paths_closed" : False,
 | |
|     }
 | |
|     def __init__(self, **kwargs):
 | |
|         VGroup.__init__(self, **kwargs)
 | |
|         rect = Rectangle(
 | |
|             height = 2.61,
 | |
|             width = 6.14,
 | |
|             color = self.color,
 | |
|             mark_paths_closed = False,
 | |
|             fill_color = BLACK,
 | |
|             fill_opacity = 1,
 | |
|         )
 | |
|         rect.set_height(self.height)
 | |
|         oval = Circle()
 | |
|         oval.stretch_to_fit_height(0.7*self.height)
 | |
|         oval.stretch_to_fit_width(0.4*self.height)
 | |
|         rect.add_subpath(oval.points)
 | |
| 
 | |
|         pi = Randolph(
 | |
|             mode = "pondering",
 | |
|             color = GREEN_B
 | |
|         )
 | |
|         pi.set_width(oval.get_width())
 | |
|         pi.move_to(oval)
 | |
|         pi.shift(0.1*pi.get_height()*DOWN)
 | |
| 
 | |
|         self.add(pi, rect)
 | |
|         for vect in UP+LEFT, DOWN+RIGHT:
 | |
|             ten = TexMobject("\\$10")
 | |
|             ten.set_height(0.25*self.height)
 | |
|             ten.next_to(self.get_corner(vect), -vect, SMALL_BUFF)
 | |
|             ten.set_color(GREEN_C)
 | |
|             self.add(ten)
 | |
| 
 | |
| 
 | |
| ##################
 | |
| 
 | |
| class AskQuestion(Scene):
 | |
|     CONFIG = {
 | |
|         "time_per_char" : 0.06,
 | |
|     }
 | |
|     def construct(self):
 | |
|         strings = [
 | |
|             "What", "does", "it", "mean   ", "to", 
 | |
|             "have    ", "a", "Bitcoin?"
 | |
|         ]
 | |
|         question = TextMobject(*strings)
 | |
|         question.set_color_by_tex("have", YELLOW)
 | |
|         self.wait()
 | |
|         for word, part in zip(strings, question):
 | |
|             n_chars = len(word.strip())
 | |
|             n_spaces = len(word) - n_chars
 | |
|             self.play(
 | |
|                 LaggedStart(FadeIn, part),
 | |
|                 run_time = self.time_per_char * len(word),
 | |
|                 rate_func = squish_rate_func(smooth, 0, 0.5)
 | |
|             )
 | |
|             self.wait(self.time_per_char*n_spaces)
 | |
|         self.wait(2)
 | |
| 
 | |
| class ListOfAttributes(Scene):
 | |
|     def construct(self):
 | |
|         logo = BitcoinLogo()
 | |
| 
 | |
|         digital = TextMobject("Digital")
 | |
|         government, bank = buildings = [
 | |
|             SVGMobject(
 | |
|                 file_name = "%s_building"%word,
 | |
|                 height = 2,
 | |
|                 fill_color = LIGHT_GREY,
 | |
|                 fill_opacity = 1,
 | |
|                 stroke_width = 0,
 | |
|             )
 | |
|             for word in ("government", "bank")
 | |
|         ]
 | |
|         attributes = VGroup(digital, *buildings)
 | |
|         attributes.arrange_submobjects(RIGHT, buff = LARGE_BUFF)
 | |
|         for building in buildings:
 | |
|             building.cross = Cross(building)
 | |
|             building.cross.set_stroke(width = 12)
 | |
| 
 | |
|         self.play(DrawBorderThenFill(logo))
 | |
|         self.play(
 | |
|             logo.to_corner, UP+LEFT,
 | |
|             Write(digital, run_time = 2)
 | |
|         )
 | |
|         for building in buildings:
 | |
|             self.play(FadeIn(building))
 | |
|             self.play(ShowCreation(building.cross))
 | |
|         self.wait()
 | |
| 
 | |
| class UnknownAuthor(Scene):
 | |
|     CONFIG = {
 | |
|         "camera_config" : {
 | |
|             "background_image" : "bitcoin_paper"
 | |
|         }
 | |
|     }
 | |
|     def construct(self):
 | |
|         rect = Rectangle(height = 0.4, width = 2.5)
 | |
|         rect.shift(2.45*UP)
 | |
|         question = TextMobject("Who is this?")
 | |
|         question.next_to(rect, RIGHT, buff = 1.5)
 | |
|         arrow = Arrow(question, rect, buff = SMALL_BUFF)
 | |
|         VGroup(question, arrow, rect).set_color(RED_D)
 | |
| 
 | |
|         self.play(ShowCreation(rect))
 | |
|         self.play(
 | |
|             Write(question),
 | |
|             ShowCreation(arrow)
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
| class DisectQuestion(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         self.hold_up_question()
 | |
|         self.list_topics()
 | |
|         self.isolate_you()
 | |
| 
 | |
|     def hold_up_question(self):
 | |
|         question = TextMobject(
 | |
|             "What does it mean to", "have", "a", "Bitcoin?"
 | |
|         )
 | |
|         question.set_color_by_tex("have", YELLOW)
 | |
|         question.next_to(self.teacher, UP)
 | |
|         question.to_edge(RIGHT, buff = LARGE_BUFF)
 | |
|         question.save_state()
 | |
|         question.shift(DOWN)
 | |
|         question.set_fill(opacity = 0)
 | |
| 
 | |
|         self.play(
 | |
|             self.teacher.change, "raise_right_hand",
 | |
|             question.restore
 | |
|         )
 | |
|         self.change_student_modes(*["pondering"]*3)
 | |
|         self.wait()
 | |
| 
 | |
|         self.bitcoin_word = question.get_part_by_tex("Bitcoin")
 | |
| 
 | |
|     def list_topics(self):
 | |
|         topics = TextMobject(
 | |
|             "Digital signatures, ",
 | |
|             "Proof of work, ",
 | |
|             "Cryptographic hash functions, \\dots"
 | |
|         )
 | |
|         topics.set_width(FRAME_WIDTH - LARGE_BUFF)
 | |
|         topics.to_edge(UP)
 | |
|         topics.set_color_by_tex("Digital", BLUE)
 | |
|         topics.set_color_by_tex("Proof", GREEN)
 | |
|         topics.set_color_by_tex("hash", YELLOW)
 | |
| 
 | |
|         for word in topics:
 | |
|             anims = [Write(word, run_time = 1)]
 | |
|             self.change_student_modes(
 | |
|                 *["confused"]*3,
 | |
|                 added_anims = anims,
 | |
|                 look_at_arg = word
 | |
|             )
 | |
| 
 | |
|     def isolate_you(self):
 | |
|         self.pi_creatures = VGroup()
 | |
|         you = self.students[1]
 | |
|         rect = FullScreenFadeRectangle()
 | |
|         words = TextMobject("Invent your own")
 | |
|         arrow = Arrow(UP, DOWN)
 | |
|         arrow.next_to(you, UP)
 | |
|         words.next_to(arrow, UP)
 | |
| 
 | |
|         self.revert_to_original_skipping_status()
 | |
|         self.play(FadeIn(rect), Animation(you))
 | |
|         self.play(
 | |
|             Write(words),
 | |
|             ShowCreation(arrow),
 | |
|             you.change, "erm", words
 | |
|         )
 | |
|         self.play(Blink(you))
 | |
|         self.wait()
 | |
| 
 | |
| class CryptocurrencyEquation(Scene):
 | |
|     def construct(self):
 | |
|         parts = TextMobject(
 | |
|             "Ledger", 
 | |
|             "- Trust",
 | |
|             "+ Cryptography",
 | |
|             "= Cryptocurrency"
 | |
|         )
 | |
|         VGroup(*parts[-1][1:]).set_color(YELLOW)
 | |
|         parts.set_width(FRAME_WIDTH - LARGE_BUFF)
 | |
| 
 | |
|         for part in parts:
 | |
|             self.play(FadeIn(part))
 | |
|             self.wait(2)
 | |
| 
 | |
| class CryptocurrencyMarketCaps(ExternallyAnimatedScene):
 | |
|     pass
 | |
| 
 | |
| class ListRecentCurrencies(Scene):
 | |
|     def construct(self):
 | |
|         footnote = TextMobject("$^*$Listed by market cap")
 | |
|         footnote.scale(0.5)
 | |
|         footnote.to_corner(DOWN+RIGHT)
 | |
|         self.add(footnote)
 | |
| 
 | |
|         logos = VGroup(
 | |
|             BitcoinLogo(),
 | |
|             EthereumLogo(),
 | |
|             ImageMobject("ripple_logo"),
 | |
|             LitecoinLogo(),
 | |
|             EthereumLogo().set_color_by_gradient(GREEN_B, GREEN_D),
 | |
|         )
 | |
|         for logo in logos:
 | |
|             logo.set_height(0.75)
 | |
|         logos.arrange_submobjects(DOWN, buff = MED_LARGE_BUFF)
 | |
|         logos.shift(LEFT)
 | |
|         logos.to_edge(UP)
 | |
|         names = list(map(
 | |
|             TextMobject, 
 | |
|             [
 | |
|                 "Bitcoin", "Ethereum", "Ripple", 
 | |
|                 "Litecoin", "Ethereum Classic"
 | |
|             ],
 | |
|         ))
 | |
|         for logo, name in zip(logos, names):
 | |
|             name.next_to(logo, RIGHT)
 | |
|             anims = []
 | |
|             if isinstance(logo, SVGMobject):
 | |
|                 anims.append(DrawBorderThenFill(logo, run_time = 1))
 | |
|             else:
 | |
|                 anims.append(FadeIn(logo))
 | |
|             anims.append(Write(name, run_time = 2))
 | |
|             self.play(*anims)
 | |
|         dots = TexMobject("\\vdots")
 | |
|         dots.next_to(logos, DOWN)
 | |
|         self.play(LaggedStart(FadeIn, dots, run_time = 1))
 | |
|         self.wait()
 | |
| 
 | |
| class Hype(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         self.teacher.change_mode("guilty")
 | |
|         phrases = list(map(TextMobject, [
 | |
|             "I want some!",
 | |
|             "I'll get rich, right?",
 | |
|             "Buy them all!"
 | |
|         ]))
 | |
|         modes = ["hooray", "conniving", "surprised"]
 | |
|         for student, phrase, mode in zip(self.students, phrases, modes):
 | |
|             bubble = SpeechBubble()
 | |
|             bubble.set_fill(BLACK, 1)
 | |
|             bubble.add_content(phrase)
 | |
|             bubble.resize_to_content()
 | |
|             bubble.pin_to(student)
 | |
|             bubble.add(phrase)
 | |
|             self.play(
 | |
|                 student.change_mode, mode,
 | |
|                 FadeIn(bubble),
 | |
|             )
 | |
|         self.wait(3)
 | |
| 
 | |
| class NoCommentOnSpeculation(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         axes = VGroup(
 | |
|             Line(0.25*LEFT, 4*RIGHT),
 | |
|             Line(0.25*DOWN, 3*UP),
 | |
|         )
 | |
|         times = np.arange(0, 4.25, 0.25)
 | |
|         prices = [
 | |
|             0.1, 0.5, 1.75, 1.5,
 | |
|             2.75, 2.2, 1.3, 0.8,
 | |
|             1.1, 1.3, 1.2, 1.4,
 | |
|             1.5, 1.7, 1.2, 1.3,
 | |
|         ]
 | |
|         graph = VMobject()
 | |
|         graph.set_points_as_corners([
 | |
|             time*RIGHT + price*UP
 | |
|             for time, price in zip(times, prices)
 | |
|         ])
 | |
|         graph.set_stroke(BLUE)
 | |
|         group = VGroup(axes, graph)
 | |
|         group.next_to(self.teacher, UP+LEFT)
 | |
| 
 | |
|         cross = Cross(group)
 | |
| 
 | |
|         mining_graphic = ImageMobject("bitcoin_mining_graphic")
 | |
|         mining_graphic.set_height(2)
 | |
|         mining_graphic.next_to(self.teacher, UP+LEFT)
 | |
|         mining_cross = Cross(mining_graphic)
 | |
|         mining_cross.set_stroke(RED, 8)
 | |
| 
 | |
|         axes.save_state()
 | |
|         axes.shift(DOWN)
 | |
|         axes.fade(1)
 | |
|         self.play(
 | |
|             self.teacher.change, "sassy",
 | |
|             axes.restore,
 | |
|         )
 | |
|         self.play(ShowCreation(
 | |
|             graph, run_time = 2,
 | |
|             rate_func = None
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(ShowCreation(cross))
 | |
|         group.add(cross)
 | |
|         self.play(
 | |
|             group.shift, FRAME_WIDTH*RIGHT,
 | |
|             self.teacher.change, "happy"
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
|         self.student_says(
 | |
|             "But...what are they?",
 | |
|             student_index = 0,
 | |
|             target_mode = "confused"
 | |
|         )
 | |
|         self.wait(2)
 | |
|         self.play(
 | |
|             FadeIn(mining_graphic),
 | |
|             RemovePiCreatureBubble(self.students[0]),
 | |
|             self.teacher.change, "sassy",
 | |
|         )
 | |
|         self.play(ShowCreation(mining_cross))
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             VGroup(mining_graphic, mining_cross).shift,
 | |
|             FRAME_WIDTH*RIGHT
 | |
|         )
 | |
|         black_words = TextMobject("Random words\\\\Blah blah")
 | |
|         black_words.set_color(BLACK)
 | |
|         self.teacher_thinks(black_words)
 | |
|         self.zoom_in_on_thought_bubble()
 | |
| 
 | |
| class MiningIsALotteryCopy(ExternallyAnimatedScene):
 | |
|     pass
 | |
| 
 | |
| class LedgerScene(PiCreatureScene):
 | |
|     CONFIG = {
 | |
|         "ledger_width" : 6,
 | |
|         "ledger_height" : 7,
 | |
|         "denomination" : "USD",
 | |
|         "ledger_line_height" : 0.4,
 | |
|         "sign_transactions" : False,
 | |
|         "enumerate_lines" : False,
 | |
|         "line_number_color" : YELLOW,
 | |
|     }
 | |
|     def setup(self):
 | |
|         PiCreatureScene.setup(self)
 | |
|         self.remove(self.pi_creatures)
 | |
| 
 | |
|     def add_ledger_and_network(self):
 | |
|         self.add(self.get_ledger(), self.get_network())
 | |
| 
 | |
|     def get_ledger(self):
 | |
|         title = TextMobject("Ledger")
 | |
|         rect = Rectangle(
 | |
|             width = self.ledger_width, 
 | |
|             height = self.ledger_height
 | |
|         )
 | |
|         title.next_to(rect.get_top(), DOWN)
 | |
|         h_line = Line(rect.get_left(), rect.get_right())
 | |
|         h_line.scale(0.8)
 | |
|         h_line.set_stroke(width = 2)
 | |
|         h_line.next_to(title, DOWN)
 | |
|         content = VGroup(h_line)
 | |
| 
 | |
|         self.ledger = VGroup(rect, title, content)
 | |
|         self.ledger.content = content
 | |
|         self.ledger.to_corner(UP+LEFT)
 | |
|         return self.ledger
 | |
| 
 | |
|     def add_line_to_ledger(self, string_or_mob):
 | |
|         if isinstance(string_or_mob, str):
 | |
|             mob = TextMobject(string_or_mob)
 | |
|         elif isinstance(string_or_mob, Mobject):
 | |
|             mob = string_or_mob
 | |
|         else:
 | |
|             raise Exception("Invalid input")
 | |
| 
 | |
|         items = self.ledger.content
 | |
| 
 | |
|         mob.set_height(self.ledger_line_height)
 | |
|         if self.enumerate_lines:
 | |
|             num = TexMobject(str(len(items)) + ".")
 | |
|             num.scale(0.8)
 | |
|             num.set_color(self.line_number_color)
 | |
|             num.next_to(mob, LEFT, MED_SMALL_BUFF)
 | |
|             mob.add_to_back(num)
 | |
|         mob.next_to(
 | |
|             items[-1], DOWN, 
 | |
|             buff = MED_SMALL_BUFF, 
 | |
|             aligned_edge = LEFT
 | |
|         )
 | |
|         if self.enumerate_lines and len(items) == 1:
 | |
|             mob.shift(MED_LARGE_BUFF * LEFT)
 | |
|         items.add(mob)
 | |
|         return mob
 | |
| 
 | |
|     def add_payment_line_to_ledger(self, from_name, to_name, amount):
 | |
|         amount_str = str(amount)
 | |
|         if self.denomination == "USD":
 | |
|             amount_str = "\\$" + amount_str
 | |
|         else:
 | |
|             amount_str += " " + self.denomination
 | |
|         line_tex_parts = [
 | |
|             from_name.capitalize(), 
 | |
|             "pays" if from_name.lower() != "you" else "pay",
 | |
|             to_name.capitalize(),
 | |
|             amount_str,
 | |
|         ]
 | |
|         if self.sign_transactions:
 | |
|             line_tex_parts.append(self.get_signature_tex())
 | |
|         line = TextMobject(*line_tex_parts)
 | |
|         for name in from_name, to_name:
 | |
|             color = self.get_color_from_name(name)
 | |
|             line.set_color_by_tex(name.capitalize(), color)
 | |
|         if self.sign_transactions:
 | |
|             from_part = line.get_part_by_tex(from_name.capitalize())
 | |
|             line[-1].set_color(from_part.get_color())
 | |
| 
 | |
|         amount_color = {
 | |
|             "USD" : GREEN,
 | |
|             "BTC" : YELLOW,
 | |
|             "LD" : YELLOW,
 | |
|         }.get(self.denomination, WHITE)
 | |
|         line.set_color_by_tex(amount_str, amount_color)
 | |
| 
 | |
|         return self.add_line_to_ledger(line)
 | |
| 
 | |
|     def get_color_from_name(self, name):
 | |
|         if hasattr(self, name.lower()):
 | |
|             creature = getattr(self, name.lower())
 | |
|             color = creature.get_color()
 | |
|             if np.mean(color.get_rgb()) < 0.5:
 | |
|                 color = average_color(color, color, WHITE)
 | |
|             return color
 | |
|         return WHITE
 | |
| 
 | |
|     def animate_payment_addition(self, *args, **kwargs):
 | |
|         line = self.add_payment_line_to_ledger(*args, **kwargs)
 | |
|         self.play(LaggedStart(
 | |
|             FadeIn, 
 | |
|             VGroup(*it.chain(*line)),
 | |
|             run_time = 1
 | |
|         ))
 | |
| 
 | |
|     def get_network(self):
 | |
|         creatures = self.pi_creatures
 | |
|         lines = VGroup(*[
 | |
|             Line(
 | |
|                 VGroup(pi1, pi1.label), VGroup(pi2, pi2.label),
 | |
|                 buff = MED_SMALL_BUFF,
 | |
|                 stroke_width = 2,
 | |
|             )
 | |
|             for pi1, pi2 in it.combinations(creatures, 2)
 | |
|         ])
 | |
|         labels = VGroup(*[pi.label for pi in creatures])
 | |
|         self.network = VGroup(creatures, labels, lines)
 | |
|         self.network.lines = lines
 | |
|         return self.network
 | |
| 
 | |
|     def create_pi_creatures(self):
 | |
|         creatures = VGroup(*[
 | |
|             PiCreature(color = color, height = 1).shift(2*vect)
 | |
|             for color, vect in zip(
 | |
|                 [BLUE_C, MAROON_D, GREY_BROWN, BLUE_E],
 | |
|                 [UP+LEFT, UP+RIGHT, DOWN+LEFT, DOWN+RIGHT],
 | |
|             )
 | |
|         ])
 | |
|         creatures.to_edge(RIGHT)
 | |
|         names = self.get_names()
 | |
|         for name, creature in zip(names, creatures):
 | |
|             setattr(self, name, creature)
 | |
|             label = TextMobject(name.capitalize())
 | |
|             label.scale(0.75)
 | |
|             label.next_to(creature, DOWN, SMALL_BUFF)
 | |
|             creature.label = label
 | |
|             if (creature.get_center() - creatures.get_center())[0] > 0:
 | |
|                 creature.flip()
 | |
|             creature.look_at(creatures.get_center())
 | |
| 
 | |
|         return creatures
 | |
| 
 | |
|     def get_names(self):
 | |
|         return ["alice", "bob", "charlie", "you"]
 | |
| 
 | |
|     def get_signature_tex(self):
 | |
|         if not hasattr(self, "nonce"):
 | |
|             self.nonce = 0
 | |
|         binary = bin(hash(str(self.nonce)))[-8:]
 | |
|         self.nonce += 1
 | |
|         return binary + "\\dots"
 | |
| 
 | |
|     def get_signature(self, color = BLUE_C):
 | |
|         result = TexMobject(self.get_signature_tex())
 | |
|         result.set_color(color)
 | |
|         return result
 | |
| 
 | |
|     def add_ellipsis(self):
 | |
|         last_item = self.ledger.content[-1]
 | |
|         dots = TexMobject("\\vdots")
 | |
|         dots.next_to(last_item.get_left(), DOWN)
 | |
|         last_item.add(dots)
 | |
|         self.add(last_item)
 | |
| 
 | |
| class LayOutPlan(LedgerScene):
 | |
|     def construct(self):
 | |
|         self.ask_question()
 | |
|         self.show_ledger()
 | |
|         self.become_skeptical()
 | |
| 
 | |
|     def ask_question(self):
 | |
|         btc = BitcoinLogo()
 | |
|         group = VGroup(btc, TexMobject("= ???"))
 | |
|         group.arrange_submobjects(RIGHT)
 | |
| 
 | |
|         self.play(
 | |
|             DrawBorderThenFill(btc),
 | |
|             Write(group[1], run_time = 2)
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             group.scale, 0.7,
 | |
|             group.next_to, ORIGIN, RIGHT,
 | |
|             group.to_edge, UP
 | |
|         )
 | |
| 
 | |
|     def show_ledger(self):
 | |
|         network = self.get_network()
 | |
|         ledger = self.get_ledger()
 | |
|         payments = [
 | |
|             ("Alice", "Bob", 20),
 | |
|             ("Bob", "Charlie", 40),
 | |
|             ("Alice", "You", 50),
 | |
|         ]
 | |
| 
 | |
|         self.play(*list(map(FadeIn, [network, ledger])))
 | |
|         for payment in payments:
 | |
|             new_line = self.add_payment_line_to_ledger(*payment)
 | |
|             from_name, to_name, amount = payment
 | |
|             from_pi = getattr(self, from_name.lower())
 | |
|             to_pi = getattr(self, to_name.lower())
 | |
|             cash = TexMobject("\\$"*(amount/10))
 | |
|             cash.scale(0.5)
 | |
|             cash.move_to(from_pi)
 | |
|             cash.set_color(GREEN)
 | |
| 
 | |
|             self.play(
 | |
|                 cash.move_to, to_pi,
 | |
|                 to_pi.change_mode, "hooray"
 | |
|             )
 | |
|             self.play(
 | |
|                 FadeOut(cash),
 | |
|                 Write(new_line, run_time = 1)
 | |
|             )
 | |
|             self.wait()
 | |
| 
 | |
|     def become_skeptical(self):
 | |
|         creatures = self.pi_creatures
 | |
| 
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change_mode, "sassy")
 | |
|             for pi in creatures
 | |
|         ])
 | |
|         for k in range(3):
 | |
|             self.play(*[
 | |
|                 ApplyMethod(
 | |
|                     creatures[i].look_at,
 | |
|                     creatures[k*(i+1)%4]
 | |
|                 )
 | |
|                 for i in range(4)
 | |
|             ])
 | |
|         self.wait(2)
 | |
| 
 | |
| class UnderlyingSystemVsUserFacing(Scene):
 | |
|     def construct(self):
 | |
|         underlying = TextMobject("Underlying \\\\ system")
 | |
|         underlying.shift(DOWN).to_edge(LEFT)
 | |
|         user_facing = TextMobject("User-facing")
 | |
|         user_facing.next_to(underlying, UP, LARGE_BUFF, LEFT)
 | |
| 
 | |
|         protocol = TextMobject("Bitcoin protocol")
 | |
|         protocol.next_to(underlying, RIGHT, MED_LARGE_BUFF)
 | |
|         protocol.set_color(BITCOIN_COLOR)
 | |
|         banking = TextMobject("Banking system")
 | |
|         banking.next_to(protocol, RIGHT, MED_LARGE_BUFF)
 | |
|         banking.set_color(GREEN)
 | |
| 
 | |
|         phone = SVGMobject(
 | |
|             file_name = "phone",
 | |
|             fill_color = WHITE,
 | |
|             fill_opacity = 1,
 | |
|             height = 1,
 | |
|             stroke_width = 0,
 | |
|         )
 | |
|         phone.next_to(protocol, UP, LARGE_BUFF)
 | |
|         card = SVGMobject(
 | |
|             file_name = "credit_card",
 | |
|             fill_color = LIGHT_GREY,
 | |
|             fill_opacity = 1,
 | |
|             stroke_width = 0,
 | |
|             height = 1
 | |
|         )
 | |
|         card.next_to(banking, UP, LARGE_BUFF)
 | |
| 
 | |
|         btc = BitcoinLogo()
 | |
|         btc.next_to(phone, UP, MED_LARGE_BUFF)
 | |
|         dollar = TexMobject("\\$")
 | |
|         dollar.set_height(1)
 | |
|         dollar.set_color(GREEN)
 | |
|         dollar.next_to(card, UP, MED_LARGE_BUFF)
 | |
|         card.save_state()
 | |
|         card.shift(2*RIGHT)
 | |
|         card.set_fill(opacity = 0)
 | |
| 
 | |
| 
 | |
|         h_line = Line(underlying.get_left(), banking.get_right())
 | |
|         h_line.next_to(underlying, DOWN, MED_SMALL_BUFF, LEFT)
 | |
|         h_line2 = h_line.copy()
 | |
|         h_line2.next_to(user_facing, DOWN, MED_LARGE_BUFF, LEFT)
 | |
|         h_line3 = h_line.copy()
 | |
|         h_line3.next_to(user_facing, UP, MED_LARGE_BUFF, LEFT)
 | |
|         v_line = Line(5*UP, ORIGIN)
 | |
|         v_line.next_to(underlying, RIGHT, MED_SMALL_BUFF)
 | |
|         v_line.shift(1.7*UP)
 | |
|         v_line2 = v_line.copy()
 | |
|         v_line2.next_to(protocol, RIGHT, MED_SMALL_BUFF)
 | |
|         v_line2.shift(1.7*UP)
 | |
| 
 | |
|         self.add(h_line, h_line2, h_line3, v_line, v_line2)
 | |
|         self.add(underlying, user_facing, btc)
 | |
|         self.play(Write(protocol))
 | |
|         self.wait(2)
 | |
|         self.play(
 | |
|             card.restore,
 | |
|             Write(dollar)
 | |
|         )
 | |
|         self.play(Write(banking))
 | |
|         self.wait(2)
 | |
|         self.play(DrawBorderThenFill(phone))
 | |
|         self.wait(2)
 | |
|         
 | |
| class FromBankToDecentralizedSystemCopy(ExternallyAnimatedScene):
 | |
|     pass
 | |
| 
 | |
| class IntroduceLedgerSystem(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "payments" : [
 | |
|             ("Alice", "Bob", 20),
 | |
|             ("Bob", "Charlie", 40),
 | |
|             ("Charlie", "You", 30),
 | |
|             ("You", "Alice", 10),
 | |
|         ]
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add(self.get_network())
 | |
|         self.exchange_money()
 | |
|         self.add_ledger()
 | |
|         self.tally_it_all_up()
 | |
| 
 | |
| 
 | |
|     def exchange_money(self):
 | |
|         for from_name, to_name, num in self.payments:
 | |
|             from_pi = getattr(self, from_name.lower())
 | |
|             to_pi = getattr(self, to_name.lower())
 | |
|             cash = TexMobject("\\$"*(num/10)).set_color(GREEN)
 | |
|             cash.set_height(0.5)
 | |
|             cash.move_to(from_pi)
 | |
|             self.play(
 | |
|                 cash.move_to, to_pi,
 | |
|                 to_pi.change_mode, "hooray"
 | |
|             )
 | |
|             self.play(FadeOut(cash))
 | |
|         self.wait()
 | |
| 
 | |
|     def add_ledger(self):
 | |
|         ledger = self.get_ledger()
 | |
| 
 | |
|         self.play(
 | |
|             Write(ledger),
 | |
|             *[
 | |
|                 ApplyMethod(pi.change, "pondering", ledger)
 | |
|                 for pi in self.pi_creatures
 | |
|             ]
 | |
|         )
 | |
|         for payment in self.payments:
 | |
|             self.animate_payment_addition(*payment)
 | |
|         self.wait(3)
 | |
| 
 | |
|     def tally_it_all_up(self):
 | |
|         accounts = dict()
 | |
|         names = "alice", "bob", "charlie", "you"
 | |
|         for name in names:
 | |
|             accounts[name] = 0
 | |
|         for from_name, to_name, amount in self.payments:
 | |
|             accounts[from_name.lower()] -= amount
 | |
|             accounts[to_name.lower()] += amount
 | |
| 
 | |
|         results = VGroup()
 | |
|         debtors = VGroup()
 | |
|         creditors = VGroup()
 | |
|         for name in names:
 | |
|             amount = accounts[name]
 | |
|             creature = getattr(self, name)
 | |
|             creature.cash = TexMobject("\\$"*abs(amount/10))
 | |
|             creature.cash.next_to(creature, UP+LEFT, SMALL_BUFF)
 | |
|             creature.cash.set_color(GREEN)
 | |
|             if amount < 0:
 | |
|                 verb = "Owes"
 | |
|                 debtors.add(creature)
 | |
|             else:
 | |
|                 verb = "Gets"
 | |
|                 creditors.add(creature)
 | |
|             if name == "you":
 | |
|                 verb = verb[:-1]
 | |
|             result = TextMobject(
 | |
|                 verb, "\\$%d"%abs(amount)
 | |
|             )
 | |
|             result.set_color_by_tex("Owe", RED)
 | |
|             result.set_color_by_tex("Get", GREEN)
 | |
|             result.add_background_rectangle()
 | |
|             result.scale(0.7)
 | |
|             result.next_to(creature.label, DOWN)
 | |
|             results.add(result)
 | |
| 
 | |
|         brace = Brace(VGroup(*self.ledger.content[1:]), RIGHT)
 | |
|         tally_up = brace.get_text("Tally up")
 | |
|         tally_up.add_background_rectangle()
 | |
| 
 | |
|         self.play(
 | |
|             GrowFromCenter(brace), 
 | |
|             FadeIn(tally_up)
 | |
|         )
 | |
|         self.play(
 | |
|             LaggedStart(FadeIn, results),
 | |
|             *[
 | |
|                 ApplyMethod(pi.change, "happy")
 | |
|                 for pi in creditors
 | |
|             ] + [
 | |
|                 ApplyMethod(pi.change, "plain")
 | |
|                 for pi in debtors
 | |
|             ]
 | |
|         )
 | |
|         self.wait()
 | |
|         debtor_cash, creditor_cash = [
 | |
|             VGroup(*it.chain(*[pi.cash for pi in group]))
 | |
|             for group in (debtors, creditors)
 | |
|         ]
 | |
|         self.play(FadeIn(debtor_cash))
 | |
|         self.play(
 | |
|             debtor_cash.arrange_submobjects, RIGHT, SMALL_BUFF,
 | |
|             debtor_cash.move_to, self.pi_creatures,
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(ReplacementTransform(
 | |
|             debtor_cash, creditor_cash
 | |
|         ))
 | |
|         self.wait(2)
 | |
| 
 | |
| class InitialProtocol(Scene):
 | |
|     def construct(self):
 | |
|         self.add_title()
 | |
|         self.show_first_two_items()
 | |
| 
 | |
|     def add_title(self):
 | |
|         title = TextMobject("Protocol")
 | |
|         title.scale(1.5)
 | |
|         title.to_edge(UP)
 | |
|         h_line = Line(LEFT, RIGHT).scale(4)
 | |
|         h_line.next_to(title, DOWN)
 | |
|         self.h_line = h_line
 | |
|         self.title = title
 | |
|         self.add(title, h_line)
 | |
| 
 | |
|     def show_first_two_items(self):
 | |
|         items = VGroup(*list(map(self.get_new_item, [
 | |
|             "Anyone can add lines to the Ledger",
 | |
|             "Settle up with real money each month"
 | |
|         ])))
 | |
| 
 | |
|         for item in items:
 | |
|             self.wait()
 | |
|             self.play(LaggedStart(FadeIn, item))
 | |
|         self.wait(2)
 | |
| 
 | |
|     def get_new_item(self, item_string):
 | |
|         item = TextMobject("$\\cdot$ %s"%item_string)
 | |
|         if not hasattr(self, "items"):
 | |
|             self.items = VGroup(item)
 | |
|             self.items.next_to(self.h_line, DOWN, MED_LARGE_BUFF)
 | |
|         else:
 | |
|             item.next_to(self.items, DOWN, MED_LARGE_BUFF, LEFT)
 | |
|             self.items.add(item)
 | |
|         return item
 | |
| 
 | |
| class AddFraudulentLine(LedgerScene):
 | |
|     def construct(self):
 | |
|         self.add_ledger_and_network()
 | |
|         self.anyone_can_add_a_line()
 | |
|         self.bob_adds_lines()
 | |
|         self.alice_reacts()
 | |
| 
 | |
|     def anyone_can_add_a_line(self):
 | |
|         words = TextMobject("Anyone can add a line")
 | |
|         words.to_corner(UP+RIGHT)
 | |
|         words.set_color(YELLOW)
 | |
|         arrow = Arrow(
 | |
|             words.get_left(), 
 | |
|             self.ledger.content.get_center() + DOWN,
 | |
|         )
 | |
| 
 | |
|         self.play(Write(words, run_time = 1))
 | |
|         self.play(ShowCreation(arrow))
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             FadeOut(words),
 | |
|             FadeOut(arrow),
 | |
|             FocusOn(self.bob),
 | |
|         )
 | |
| 
 | |
|     def bob_adds_lines(self):
 | |
|         line = self.add_payment_line_to_ledger("Alice", "Bob", 100)
 | |
|         line.save_state()
 | |
|         line.scale(0.001)
 | |
|         line.move_to(self.bob)
 | |
| 
 | |
|         self.play(self.bob.change, "conniving")
 | |
|         self.play(line.restore)
 | |
|         self.wait()
 | |
| 
 | |
|     def alice_reacts(self):
 | |
|         bubble = SpeechBubble(
 | |
|             height = 1.5, width = 2, direction = LEFT,
 | |
|         )
 | |
|         bubble.next_to(self.alice, UP+RIGHT, buff = 0)
 | |
|         bubble.write("Hey!")
 | |
|         self.play(
 | |
|             Animation(self.bob.pupils),
 | |
|             self.alice.change, "angry",
 | |
|             FadeIn(bubble),
 | |
|             Write(bubble.content, run_time = 1)
 | |
|         )
 | |
|         self.wait(3)
 | |
|         self.play(
 | |
|             FadeOut(bubble),
 | |
|             FadeOut(bubble.content),
 | |
|             self.alice.change_mode, "pondering"
 | |
|         )
 | |
| 
 | |
| class AnnounceDigitalSignatures(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         words = TextMobject("Digital \\\\ signatures!")
 | |
|         words.scale(1.5)
 | |
|         self.teacher_says(
 | |
|             words,
 | |
|             target_mode = "hooray",
 | |
|         )
 | |
|         self.change_student_modes(*["hooray"]*3)
 | |
|         self.wait(2)
 | |
| 
 | |
| class IntroduceSignatures(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "payments" : [
 | |
|             ("Alice", "Bob", 100),
 | |
|             ("Charlie", "You", 20),
 | |
|             ("Bob", "You", 30),
 | |
|         ],
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_ledger_and_network()
 | |
|         self.add_transactions()
 | |
|         self.add_signatures()
 | |
| 
 | |
|     def add_transactions(self):
 | |
|         transactions = VGroup(*[
 | |
|             self.add_payment_line_to_ledger(*payment)
 | |
|             for payment in self.payments
 | |
|         ])
 | |
|         self.play(LaggedStart(FadeIn, transactions))
 | |
|         self.wait()
 | |
| 
 | |
|     def add_signatures(self):
 | |
|         signatures = VGroup(*[
 | |
|             get_cursive_name(payments[0].capitalize())
 | |
|             for payments in self.payments
 | |
|         ])
 | |
|         for signature, transaction in zip(signatures, self.ledger.content[1:]):
 | |
|             signature.next_to(transaction, RIGHT)
 | |
|             signature.set_color(transaction[0].get_color())
 | |
|             self.play(Write(signature, run_time = 2))
 | |
|             transaction.add(signature)
 | |
|         self.wait(2)
 | |
| 
 | |
|         rect = SurroundingRectangle(self.ledger.content[1])
 | |
|         self.play(ShowCreation(rect))
 | |
|         self.play(FadeOut(rect))
 | |
|         self.wait()
 | |
|         self.play(Indicate(signatures[0]))
 | |
|         self.wait()
 | |
| 
 | |
| class AskHowDigitalSignaturesArePossible(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         signature = get_cursive_name("Alice")
 | |
|         signature.scale(1.5)
 | |
|         signature.set_color(BLUE_C)
 | |
|         signature.to_corner(UP+LEFT)
 | |
|         signature_copy = signature.copy()
 | |
|         signature_copy.shift(3*RIGHT)
 | |
| 
 | |
|         bits = TexMobject("01100001")
 | |
|         bits.next_to(signature, DOWN)
 | |
|         bits.shift_onto_screen()
 | |
|         bits_copy = bits.copy()
 | |
|         bits_copy.next_to(signature_copy, DOWN)
 | |
| 
 | |
| 
 | |
|         self.student_says(
 | |
|             "Couldn't you just \\\\ copy the signature?",
 | |
|             target_mode = "confused",
 | |
|             run_time = 1
 | |
|         )
 | |
|         self.change_student_modes("pondering", "confused", "erm")
 | |
|         self.play(Write(signature))
 | |
|         self.play(LaggedStart(FadeIn, bits, run_time = 1))
 | |
|         self.wait()
 | |
|         self.play(ReplacementTransform(
 | |
|             bits.copy(), bits_copy,
 | |
|             path_arc = np.pi/2
 | |
|         ))
 | |
|         self.play(Write(signature_copy))
 | |
|         self.wait(3)
 | |
| 
 | |
| class DescribeDigitalSignatures(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "public_color" : GREEN,
 | |
|         "private_color" : RED,
 | |
|         "signature_color" : BLUE_C,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.reorganize_pi_creatures()
 | |
|         self.generate_key_pairs()
 | |
|         self.keep_secret_key_secret()
 | |
|         self.show_handwritten_signatures()
 | |
|         self.show_digital_signatures()
 | |
|         self.show_signing_functions()
 | |
| 
 | |
|     def reorganize_pi_creatures(self):
 | |
|         self.pi_creatures.remove(self.you)
 | |
|         creature_groups = VGroup(*[
 | |
|             VGroup(pi, pi.label).scale(1.7)
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
|         creature_groups.arrange_submobjects(RIGHT, buff = 2)
 | |
|         creature_groups.to_edge(DOWN)
 | |
|         self.add(creature_groups)
 | |
|         for pi in self.pi_creatures:
 | |
|             if pi.is_flipped():
 | |
|                 pi.flip()
 | |
| 
 | |
|     def generate_key_pairs(self):
 | |
|         title = TextMobject("Private", "key /", "Public", "key")
 | |
|         title.to_edge(UP)
 | |
|         private, public = list(map(title.get_part_by_tex, ["Private", "Public"]))
 | |
|         private.set_color(self.private_color)
 | |
|         public.set_color(self.public_color)
 | |
|         secret = TextMobject("Secret")
 | |
|         secret.move_to(private, RIGHT)
 | |
|         secret.set_color(self.private_color)
 | |
| 
 | |
|         names = self.get_names()[:-1]
 | |
|         public_key_strings = [
 | |
|             bin(256+ord(name[0].capitalize()))[3:]
 | |
|             for name in names
 | |
|         ]
 | |
|         private_key_strings = [
 | |
|             bin(hash(name))[2:10]
 | |
|             for name in names
 | |
|         ]
 | |
|         public_keys, private_keys = [
 | |
|             VGroup(*[
 | |
|                 TextMobject(key_name+":"," $%s\\dots$"%key)
 | |
|                 for key in keys
 | |
|             ])
 | |
|             for key_name, keys in [
 | |
|                 ("pk", public_key_strings),
 | |
|                 ("sk", private_key_strings)
 | |
|             ]
 | |
|         ]
 | |
|         key_pairs = [
 | |
|             VGroup(*pair).arrange_submobjects(DOWN, aligned_edge = LEFT)
 | |
|             for pair in zip(public_keys, private_keys)
 | |
|         ]
 | |
|         for key_pair, pi in zip(key_pairs, self.pi_creatures):
 | |
|             key_pair.next_to(pi, UP, MED_LARGE_BUFF)
 | |
|             for key in key_pair:
 | |
|                 key.set_color_by_tex("sk", self.private_color)
 | |
|                 key.set_color_by_tex("pk", self.public_color)
 | |
| 
 | |
|         self.play(Write(title, run_time = 2))
 | |
|         self.play(ReplacementTransform(
 | |
|             VGroup(VGroup(public.copy())),
 | |
|             public_keys
 | |
|         ))
 | |
|         self.play(ReplacementTransform(
 | |
|             VGroup(VGroup(private.copy())),
 | |
|             private_keys
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(private.shift, DOWN)
 | |
|         self.play(FadeIn(secret))
 | |
|         self.play(FadeOut(private))
 | |
|         self.wait()
 | |
| 
 | |
|         title.remove(private)
 | |
|         title.add(secret)
 | |
|         self.title = title
 | |
|         self.private_keys = private_keys
 | |
|         self.public_keys = public_keys
 | |
| 
 | |
|     def keep_secret_key_secret(self):
 | |
|         keys = self.private_keys
 | |
|         rects = VGroup(*list(map(SurroundingRectangle, keys)))
 | |
|         rects.set_color(self.private_color)
 | |
|         lock = SVGMobject(
 | |
|             file_name = "lock",
 | |
|             height = rects.get_height(),
 | |
|             fill_color = LIGHT_GREY,
 | |
|             fill_opacity = 1,
 | |
|             stroke_width = 0,
 | |
|         )
 | |
|         locks = VGroup(*[
 | |
|             lock.copy().next_to(rect, LEFT, SMALL_BUFF)
 | |
|             for rect in rects
 | |
|         ])
 | |
| 
 | |
|         self.play(ShowCreation(rects))
 | |
|         self.play(LaggedStart(DrawBorderThenFill, locks))
 | |
|         self.wait()
 | |
| 
 | |
|         self.private_key_rects = rects
 | |
|         self.locks = locks
 | |
| 
 | |
|     def show_handwritten_signatures(self):
 | |
|         lines = VGroup(*[Line(LEFT, RIGHT) for x in range(5)])
 | |
|         lines.arrange_submobjects(DOWN)
 | |
|         last_line = lines[-1]
 | |
|         last_line.scale(0.7, about_point = last_line.get_left())
 | |
| 
 | |
|         signature_line = lines[0].copy()
 | |
|         signature_line.set_stroke(width = 2)
 | |
|         signature_line.next_to(lines, DOWN, LARGE_BUFF)
 | |
|         ex = TexMobject("\\times")
 | |
|         ex.scale(0.7)
 | |
|         ex.next_to(signature_line, UP, SMALL_BUFF, LEFT)
 | |
|         lines.add(ex, signature_line)
 | |
| 
 | |
|         rect = SurroundingRectangle(
 | |
|             lines, 
 | |
|             color = LIGHT_GREY, 
 | |
|             buff = MED_SMALL_BUFF
 | |
|         )
 | |
|         document = VGroup(rect, lines)
 | |
|         documents = VGroup(*[
 | |
|             document.copy()
 | |
|             for x in range(2)
 | |
|         ])
 | |
|         documents.arrange_submobjects(RIGHT, buff = MED_LARGE_BUFF)
 | |
|         documents.to_corner(UP+LEFT)
 | |
| 
 | |
|         signatures = VGroup()
 | |
|         for document in documents:
 | |
|             signature = get_cursive_name("Alice")
 | |
|             signature.set_color(self.signature_color)
 | |
|             line = document[1][-1]
 | |
|             signature.next_to(line, UP, SMALL_BUFF)
 | |
|             signatures.add(signature)
 | |
| 
 | |
|         self.play(
 | |
|             FadeOut(self.title),
 | |
|             LaggedStart(FadeIn, documents, run_time = 1)
 | |
|         )
 | |
|         self.play(Write(signatures))
 | |
|         self.wait()
 | |
| 
 | |
|         self.signatures = signatures
 | |
|         self.documents = documents
 | |
| 
 | |
|     def show_digital_signatures(self):
 | |
|         rect = SurroundingRectangle(VGroup(
 | |
|             self.public_keys[0],
 | |
|             self.private_key_rects[0],
 | |
|             self.locks[0]
 | |
|         ))
 | |
|         digital_signatures = VGroup()
 | |
|         for i, signature in enumerate(self.signatures):
 | |
|             bits = bin(hash(str(i)))[-8:]
 | |
|             digital_signature = TexMobject(bits + "\\dots")
 | |
|             digital_signature.scale(0.7)
 | |
|             digital_signature.set_color(signature.get_color())
 | |
|             digital_signature.move_to(signature, DOWN)
 | |
|             digital_signatures.add(digital_signature)
 | |
| 
 | |
|         arrows = VGroup(*[
 | |
|             Arrow(
 | |
|                 rect.get_corner(UP), sig.get_bottom(),
 | |
|                 tip_length = 0.15,
 | |
|                 color = WHITE
 | |
|             )
 | |
|             for sig in digital_signatures
 | |
|         ])
 | |
| 
 | |
|         words = VGroup(*list(map(
 | |
|             TextMobject,
 | |
|             ["Different messages", "Completely different signatures"]
 | |
|         )))
 | |
|         words.arrange_submobjects(DOWN, aligned_edge = LEFT)
 | |
|         words.scale(1.3)
 | |
|         words.next_to(self.documents, RIGHT)
 | |
| 
 | |
|         self.play(FadeIn(rect))
 | |
|         self.play(*list(map(ShowCreation, arrows)))
 | |
|         self.play(Transform(self.signatures, digital_signatures))
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change, "pondering", digital_signatures)
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
|         for word in words:
 | |
|             self.play(FadeIn(word))
 | |
|         self.wait()
 | |
|         self.play(FadeOut(words))
 | |
| 
 | |
|     def show_signing_functions(self):
 | |
|         sign = TextMobject(
 | |
|             "Sign(", "Message", ", ", "sk", ") = ", "Signature",
 | |
|             arg_separator = ""
 | |
|         )
 | |
|         sign.to_corner(UP+RIGHT)
 | |
|         verify = TextMobject(
 | |
|             "Verify(", "Message", ", ", "Signature", ", ", "pk", ") = ", "T/F",
 | |
|             arg_separator = ""
 | |
|         )
 | |
|         for mob in sign, verify:
 | |
|             mob.set_color_by_tex("sk", self.private_color)
 | |
|             mob.set_color_by_tex("pk", self.public_color)
 | |
|             mob.set_color_by_tex(
 | |
|                 "Signature", self.signature_color,
 | |
|             )
 | |
|             for name in "Message", "sk", "Signature", "pk":
 | |
|                 part = mob.get_part_by_tex(name)
 | |
|                 if part is not None:
 | |
|                     setattr(mob, name.lower(), part)
 | |
|         verify.next_to(sign, DOWN, MED_LARGE_BUFF, LEFT)
 | |
|         VGroup(sign, verify).to_corner(UP+RIGHT)
 | |
| 
 | |
|         private_key = self.private_key_rects[0]
 | |
|         public_key = self.public_keys[0]
 | |
|         message = self.documents[0]
 | |
|         signature = self.signatures[0]
 | |
| 
 | |
|         self.play(*[
 | |
|             FadeIn(part)
 | |
|             for part in sign
 | |
|             if part not in [sign.message, sign.sk, sign.signature]
 | |
|         ])
 | |
|         self.play(ReplacementTransform(
 | |
|             message.copy(), VGroup(sign.message)
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(ReplacementTransform(
 | |
|             private_key.copy(), sign.sk
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(ReplacementTransform(
 | |
|             VGroup(sign.sk, sign.message).copy(),
 | |
|             VGroup(sign.signature)
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(Indicate(sign.sk))
 | |
|         self.wait()
 | |
|         self.play(Indicate(sign.message))
 | |
|         self.wait()
 | |
|         self.play(*[
 | |
|             FadeIn(part)
 | |
|             for part in verify
 | |
|             if part not in [
 | |
|                 verify.message, verify.signature, 
 | |
|                 verify.pk, verify[-1]
 | |
|             ]
 | |
|         ])
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             ReplacementTransform(
 | |
|                 sign.message.copy(), verify.message
 | |
|             ),
 | |
|             ReplacementTransform(
 | |
|                 sign.signature.copy(), verify.signature
 | |
|             )
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(ReplacementTransform(
 | |
|             public_key.copy(), VGroup(verify.pk)
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(Write(verify[-1]))
 | |
|         self.wait()
 | |
| 
 | |
| class TryGuessingDigitalSignature(Scene):
 | |
|     def construct(self):
 | |
|         verify = TextMobject(
 | |
|             "Verify(", "Message", ", ", 
 | |
|             "256 bit Signature", ", ", "pk", ")",
 | |
|             arg_separator = ""
 | |
|         )
 | |
|         verify.scale(1.5)
 | |
|         verify.shift(DOWN)
 | |
|         signature = verify.get_part_by_tex("Signature")
 | |
|         verify.set_color_by_tex("Signature", BLUE)
 | |
|         verify.set_color_by_tex("pk", GREEN)
 | |
|         brace = Brace(signature, UP)
 | |
| 
 | |
|         zeros_row = TexMobject("0"*32)
 | |
|         zeros = VGroup(*[zeros_row.copy() for x in range(8)])
 | |
|         zeros.arrange_submobjects(DOWN, buff = SMALL_BUFF)
 | |
|         zeros.next_to(brace, UP)
 | |
| 
 | |
|         self.add(verify)
 | |
|         self.play(
 | |
|             GrowFromCenter(brace),
 | |
|             FadeIn(
 | |
|                 zeros,
 | |
|                 submobject_mode = "lagged_start",
 | |
|                 run_time = 3
 | |
|             )
 | |
|         )
 | |
|         self.wait()
 | |
|         for n in range(2**10):
 | |
|             last_row = zeros[-1]
 | |
|             binary = bin(n)[2:]
 | |
|             for i, bit_str in enumerate(reversed(binary)):
 | |
|                 curr_bit = last_row.submobjects[-i-1]
 | |
|                 new_bit = TexMobject(bit_str)
 | |
|                 new_bit.replace(curr_bit, dim_to_match = 1)
 | |
|                 last_row.submobjects[-i-1] = new_bit
 | |
|                 self.remove(curr_bit)
 | |
|             self.add(last_row)
 | |
|             self.wait(1./30)
 | |
| 
 | |
| class WriteTwoTo256PossibleSignatures(Scene):
 | |
|     def construct(self):
 | |
|         words = TextMobject(
 | |
|             "$2^{256}$", "possible\\\\", "signatures"
 | |
|         )
 | |
|         words.scale(2)
 | |
|         words.set_color_by_tex("256", BLUE)
 | |
|         self.play(Write(words))
 | |
|         self.wait()
 | |
| 
 | |
| class SupplementVideoWrapper(Scene):
 | |
|     def construct(self):
 | |
|         title = TextMobject("How secure is 256 bit security?")
 | |
|         title.scale(1.5)
 | |
|         title.to_edge(UP)
 | |
|         rect = ScreenRectangle(height = 6)
 | |
|         rect.next_to(title, DOWN)
 | |
|         self.add(title)
 | |
|         self.play(ShowCreation(rect))
 | |
|         self.wait()
 | |
| 
 | |
| class FeelConfidentWithVerification(PiCreatureScene):
 | |
|     def construct(self):
 | |
|         self.show_verification()
 | |
|         self.show_secret_key()
 | |
| 
 | |
|     def show_verification(self):
 | |
|         verify = TextMobject(
 | |
|             "Verify(", "Message", ", ", 
 | |
|             "256 bit Signature", ", ", "pk", ")",
 | |
|             arg_separator = ""
 | |
|         )
 | |
|         signature_word = verify.get_part_by_tex("Signature")
 | |
|         verify.set_color_by_tex("Signature", BLUE)
 | |
|         verify.set_color_by_tex("pk", GREEN)
 | |
|         brace = Brace(signature_word, UP)
 | |
|         signature = sha256_tex_mob("Signature")
 | |
|         signature.next_to(brace, UP)
 | |
|         signature.set_color(BLUE_C)
 | |
| 
 | |
|         rhs = TextMobject("=", "True")
 | |
|         rhs.set_color_by_tex("True", YELLOW)
 | |
|         rhs.next_to(verify, RIGHT)
 | |
| 
 | |
|         pk = verify.get_part_by_tex("pk")
 | |
|         sk = TextMobject("sk")
 | |
|         sk.set_color(RED)
 | |
|         arrow = TexMobject("\\Updownarrow")
 | |
|         arrow.next_to(pk, DOWN)
 | |
|         sk.next_to(arrow, DOWN)
 | |
|         sk_group = VGroup(arrow, sk)
 | |
|         lock_box = SurroundingRectangle(sk_group, buff = SMALL_BUFF)
 | |
|         lock_box.set_color(RED)
 | |
|         lock = SVGMobject(
 | |
|             file_name = "lock", 
 | |
|             fill_color = LIGHT_GREY,
 | |
|             height = 0.5,
 | |
|         )
 | |
|         lock.next_to(lock_box, LEFT, SMALL_BUFF)
 | |
| 
 | |
|         self.add(verify)
 | |
|         self.play(
 | |
|             GrowFromCenter(brace),
 | |
|             Write(signature),
 | |
|             self.pi_creature.change, "pondering"
 | |
|         )
 | |
|         self.play(ReplacementTransform(
 | |
|             verify.copy(), rhs
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(self.pi_creature.change, "happy")
 | |
|         self.play(Write(sk_group))
 | |
|         self.play(
 | |
|             ShowCreation(lock_box),
 | |
|             DrawBorderThenFill(lock, run_time = 1)
 | |
|         )
 | |
|         self.wait(2)
 | |
| 
 | |
| 
 | |
|     def show_secret_key(self):
 | |
|         pass
 | |
| 
 | |
| 
 | |
|     #####
 | |
| 
 | |
|     def create_pi_creature(self):
 | |
|         return Randolph().to_corner(DOWN+LEFT)
 | |
| 
 | |
| class IncludeTransactionNumber(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "ledger_width" : 7,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_ledger_and_network()
 | |
|         self.add_signed_payment()
 | |
|         self.fail_to_sign_new_transaction()
 | |
|         self.copy_payment_many_times()
 | |
|         self.add_ids()
 | |
| 
 | |
|     def add_signed_payment(self):
 | |
|         line = self.add_payment_line_to_ledger(
 | |
|             "Alice", "Bob", 100
 | |
|         )
 | |
|         signature = self.get_signature()
 | |
|         signature.scale(0.7)
 | |
|         signature.next_to(line, RIGHT)
 | |
|         signature.save_state()
 | |
|         signature.scale(0.1)
 | |
|         signature.move_to(self.alice)
 | |
| 
 | |
|         self.play(Write(line, run_time = 1))
 | |
|         self.play(
 | |
|             signature.restore,
 | |
|             self.alice.change, "raise_left_hand"
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(self.alice.change, "happy")
 | |
|         self.wait()
 | |
| 
 | |
|         line.add(signature)
 | |
| 
 | |
|     def fail_to_sign_new_transaction(self):
 | |
|         payment = self.add_payment_line_to_ledger("Alice", "Bob", 3000)
 | |
|         q_marks = TexMobject("???")
 | |
|         q_marks.next_to(payment, RIGHT)
 | |
|         cross = Cross(payment)
 | |
|         payment.save_state()
 | |
|         payment.move_to(self.bob.get_corner(UP+LEFT))
 | |
|         payment.set_fill(opacity = 0)
 | |
| 
 | |
|         self.play(
 | |
|             self.bob.change, "raise_right_hand",
 | |
|             payment.restore,
 | |
|         )
 | |
|         self.play(
 | |
|             self.bob.change, "confused",
 | |
|             Write(q_marks)
 | |
|         )
 | |
|         self.play(ShowCreation(cross))
 | |
|         self.wait()
 | |
|         self.play(*list(map(FadeOut, [payment, cross, q_marks])))
 | |
|         self.ledger.content.remove(payment)
 | |
| 
 | |
|     def copy_payment_many_times(self):
 | |
|         line = self.ledger.content[-1]
 | |
|         copies = VGroup(*[line.copy() for x in range(4)])
 | |
|         copies.arrange_submobjects(DOWN, buff = MED_SMALL_BUFF)
 | |
|         copies.next_to(line, DOWN, buff = MED_SMALL_BUFF)
 | |
| 
 | |
|         self.play(
 | |
|             LaggedStart(FadeIn, copies, run_time = 3),
 | |
|             self.bob.change, "conniving",
 | |
|         )
 | |
|         self.play(self.alice.change, "angry")
 | |
|         self.wait()
 | |
| 
 | |
|         self.copies = copies
 | |
| 
 | |
|     def add_ids(self):
 | |
|         top_line = self.ledger.content[-1]
 | |
|         lines = VGroup(top_line, *self.copies)
 | |
|         numbers = VGroup()
 | |
|         old_signatures = VGroup()
 | |
|         new_signatures = VGroup()
 | |
|         colors = list(Color(BLUE_B).range_to(GREEN_B, len(lines)))
 | |
|         for i, line in enumerate(lines):
 | |
|             number = TexMobject(str(i))
 | |
|             number.scale(0.7)
 | |
|             number.set_color(YELLOW)
 | |
|             number.next_to(line, LEFT)
 | |
|             numbers.add(number)
 | |
|             line.add_to_back(number)
 | |
|             old_signature = line[-1]
 | |
|             new_signature = self.get_signature()
 | |
|             new_signature.replace(old_signature)
 | |
|             new_signature.set_color(colors[i])
 | |
|             old_signatures.add(old_signature)
 | |
|             new_signatures.add(VGroup(new_signature))
 | |
|             line.remove(old_signature)
 | |
| 
 | |
|         self.play(
 | |
|             Write(numbers),
 | |
|             self.alice.change, "thinking"
 | |
|         )
 | |
|         self.play(FadeOut(old_signatures))
 | |
|         self.play(ReplacementTransform(
 | |
|             lines.copy(), new_signatures,
 | |
|             submobject_mode = "lagged_start",
 | |
|             run_time = 2,
 | |
|         ))
 | |
|         self.play(self.bob.change, "erm")
 | |
|         self.wait(2)
 | |
| 
 | |
| class ProtocolWithDigitalSignatures(InitialProtocol):
 | |
|     def construct(self):
 | |
|         self.force_skipping()
 | |
|         InitialProtocol.construct(self)
 | |
|         self.revert_to_original_skipping_status()
 | |
| 
 | |
|         rect = SurroundingRectangle(self.items[-1])
 | |
|         rect.set_color(RED)
 | |
| 
 | |
|         new_item = self.get_new_item(
 | |
|             "Only signed transactions are valid"
 | |
|         )
 | |
|         new_item.set_color(YELLOW)
 | |
| 
 | |
|         self.play(Write(new_item))
 | |
|         self.wait()
 | |
|         self.play(ShowCreation(rect))
 | |
|         self.wait()
 | |
| 
 | |
| class SignedLedgerScene(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "sign_transactions" : True,
 | |
|         "enumerate_lines" : True,
 | |
|         "ledger_width" : 7.5,
 | |
|     }
 | |
| 
 | |
| class CharlieRacksUpDebt(SignedLedgerScene):
 | |
|     CONFIG = {
 | |
|         "payments" : [
 | |
|             ("Charlie", "Alice", 100),
 | |
|             ("Charlie", "Bob", 200),
 | |
|             ("Charlie", "You", 800),
 | |
|             ("Charlie", "Bob", 600),
 | |
|             ("Charlie", "Alice", 900),
 | |
|         ],
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_ledger_and_network()
 | |
|         lines = VGroup(*[
 | |
|             self.add_payment_line_to_ledger(*payment)
 | |
|             for payment in self.payments
 | |
|         ])
 | |
| 
 | |
|         self.play(LaggedStart(
 | |
|             FadeIn, lines, 
 | |
|             run_time = 3,
 | |
|             lag_ratio = 0.25
 | |
|         ))
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change, "sassy", self.charlie)
 | |
|             for pi in self.pi_creatures
 | |
|             if pi is not self.charlie
 | |
|         ])
 | |
|         self.play(
 | |
|             self.charlie.shift, FRAME_X_RADIUS*RIGHT,
 | |
|             rate_func = running_start
 | |
|         )
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change, "angry", self.charlie)
 | |
|             for pi in self.get_pi_creatures()
 | |
|         ])
 | |
|         self.wait()
 | |
| 
 | |
| class CharlieFeelsGuilty(Scene):
 | |
|     def construct(self):
 | |
|         charlie = PiCreature(color = GREY_BROWN)
 | |
|         charlie.scale(2)
 | |
| 
 | |
|         self.play(FadeIn(charlie))
 | |
|         self.play(charlie.change, "sad")
 | |
|         for x in range(2):
 | |
|             self.play(Blink(charlie))
 | |
|             self.wait(2)
 | |
| 
 | |
| class ThinkAboutSettlingUp(Scene):
 | |
|     def construct(self):
 | |
|         randy = Randolph()
 | |
|         randy.to_corner(DOWN+LEFT)
 | |
| 
 | |
|         self.play(PiCreatureBubbleIntroduction(
 | |
|             randy, 
 | |
|             "You don't \\emph{actually} \\\\" + \
 | |
|             "need to settle up $\\dots$",
 | |
|             bubble_class = ThoughtBubble,
 | |
|             target_mode = "thinking"
 | |
|         ))
 | |
|         self.play(Blink(randy))
 | |
|         self.wait()
 | |
| 
 | |
| class DontAllowOverdrawing(InitialProtocol):
 | |
|     def construct(self):
 | |
|         self.add_title()
 | |
|         lines = list(map(self.get_new_item, [
 | |
|             "Anyone can add lines to the Ledger \\,",
 | |
|             "Only signed transactions are valid \\,",
 | |
|             "No overspending"
 | |
|         ]))
 | |
|         lines[2].set_color(YELLOW)
 | |
| 
 | |
|         self.add(*lines[:2])
 | |
|         self.wait()
 | |
|         self.play(Write(lines[2]))
 | |
|         self.wait()
 | |
| 
 | |
| class LedgerWithInitialBuyIn(SignedLedgerScene):
 | |
|     def construct(self):
 | |
|         self.add_ledger_and_network()
 | |
|         self.everyone_buys_in()
 | |
|         self.add_initial_lines()
 | |
|         self.add_charlie_payments()
 | |
|         self.point_out_charlie_is_broke()
 | |
|         self.running_balance()
 | |
| 
 | |
|     def everyone_buys_in(self):
 | |
|         center = self.network.get_center()
 | |
|         moneys = VGroup(*[
 | |
|             TexMobject("\\$100")
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
|         moneys.set_color(GREEN)
 | |
|         for pi, money in zip(reversed(self.pi_creatures), moneys):
 | |
|             vect = pi.get_center() - center
 | |
|             money.next_to(center, vect, SMALL_BUFF)
 | |
|             money.add_background_rectangle()
 | |
|             money.save_state()
 | |
|             money.scale(0.01)
 | |
|             corner = pi.get_corner(UP + np.sign(vect[0])*LEFT)
 | |
|             money.move_to(corner)
 | |
| 
 | |
|         self.play(
 | |
|             LaggedStart(
 | |
|                 ApplyMethod, moneys,
 | |
|                 lambda m : (m.restore,)
 | |
|             ),
 | |
|             self.charlie.change, "raise_right_hand",
 | |
|             self.you.change, "raise_right_hand",
 | |
|         )
 | |
|         self.network.add(moneys)
 | |
| 
 | |
|     def add_initial_lines(self):
 | |
|         lines = VGroup()
 | |
|         for name in self.get_names():
 | |
|             new_line = TextMobject(
 | |
|                 name.capitalize(), 
 | |
|                 "get" if name == "you" else "gets",
 | |
|                 "\\$100"
 | |
|             )
 | |
|             new_line.set_color_by_tex(
 | |
|                 name.capitalize(),
 | |
|                 self.get_color_from_name(name)
 | |
|             )
 | |
|             new_line.set_color_by_tex("100", GREEN)
 | |
|             self.add_line_to_ledger(new_line)
 | |
|             lines.add(new_line)
 | |
|         line = Line(LEFT, RIGHT)
 | |
|         line.set_width(self.ledger.get_width())
 | |
|         line.scale_in_place(0.9)
 | |
|         line.next_to(lines[-1], DOWN, SMALL_BUFF, LEFT)
 | |
|         line.set_stroke(width = 1)
 | |
|         lines[-1].add(line)
 | |
| 
 | |
|         self.play(
 | |
|             LaggedStart(FadeIn, lines),
 | |
|             *[
 | |
|                 ApplyMethod(pi.change, "thinking", self.ledger)
 | |
|                 for pi in self.pi_creatures
 | |
|             ]
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
|     def add_charlie_payments(self):
 | |
|         payments = [
 | |
|             ("Charlie", "Alice", 50),
 | |
|             ("Charlie", "Bob", 50),
 | |
|             ("Charlie", "You", 20),
 | |
|         ]
 | |
|         new_lines = VGroup(*[
 | |
|             self.add_payment_line_to_ledger(*payment)
 | |
|             for payment in payments
 | |
|         ])
 | |
| 
 | |
|         for line in new_lines:
 | |
|             self.play(Write(line, run_time = 1))
 | |
|             self.wait()
 | |
| 
 | |
|     def point_out_charlie_is_broke(self):
 | |
|         charlie_lines = VGroup(*[
 | |
|             VGroup(*self.ledger.content[i][1:5])
 | |
|             for i in (3, 5, 6, 7)
 | |
|         ])
 | |
|         rects = VGroup(*[
 | |
|             SurroundingRectangle(line)
 | |
|             for line in charlie_lines
 | |
|         ])
 | |
|         rects.set_color(YELLOW)
 | |
|         rects.set_stroke(width = 2)
 | |
|         last_rect = rects[-1]
 | |
|         last_rect.set_stroke(RED, 4)
 | |
|         rects.remove(last_rect)
 | |
|         invalid = TextMobject("Invalid")
 | |
|         invalid.set_color(RED)
 | |
|         invalid.next_to(last_rect, DOWN)
 | |
| 
 | |
|         self.play(ShowCreation(rects))
 | |
|         self.play(self.charlie.change_mode, "guilty")
 | |
|         self.wait()
 | |
|         self.play(ShowCreation(last_rect))
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change, "sassy", self.charlie)
 | |
|             for pi in self.pi_creatures
 | |
|             if pi is not self.charlie
 | |
|         ])
 | |
|         self.play(Write(invalid))
 | |
|         self.wait(2)
 | |
|         self.play(*list(map(FadeOut, [rects, last_rect, invalid])))
 | |
| 
 | |
|     def running_balance(self):
 | |
|         charlie_lines = VGroup(*[
 | |
|             VGroup(*self.ledger.content[i][1:5])
 | |
|             for i in (3, 5, 6, 7)
 | |
|         ])
 | |
|         signatures = VGroup(*[
 | |
|             self.ledger.content[i][5]
 | |
|             for i in (5, 6, 7)
 | |
|         ])
 | |
|         rect = Rectangle(color = WHITE)
 | |
|         rect.set_fill(BLACK, 0.8)
 | |
|         rect.stretch_to_fit_height(self.ledger.get_height() - 2*MED_SMALL_BUFF)
 | |
|         title = TextMobject("Charlie's running \\\\ balance")
 | |
|         rect.stretch_to_fit_width(title.get_width() + 2*MED_SMALL_BUFF)
 | |
|         rect.move_to(self.ledger.get_right())
 | |
|         title.next_to(rect.get_top(), DOWN)
 | |
|         balance = VGroup(rect, title)
 | |
| 
 | |
|         lines = VGroup(*list(map(TextMobject, [
 | |
|             "\\$100", "\\$50", "\\$0", "Overdrawn"
 | |
|         ])))
 | |
|         lines.set_color(GREEN)
 | |
|         lines[-1].set_color(RED)
 | |
|         arrows = VGroup()
 | |
|         for line, c_line in zip(lines, charlie_lines):
 | |
|             line.next_to(rect.get_left(), RIGHT, LARGE_BUFF)
 | |
|             line.shift(
 | |
|                 (c_line.get_center() - line.get_center())[1]*UP
 | |
|             )
 | |
|             arrow = Arrow(c_line, line)
 | |
|             arrows.add(arrow)
 | |
| 
 | |
|         self.pi_creatures.remove(self.alice, self.charlie)
 | |
|         self.play(
 | |
|             FadeOut(signatures),
 | |
|             FadeIn(balance)
 | |
|         )
 | |
|         self.play(
 | |
|             LaggedStart(FadeIn, lines, run_time = 3),
 | |
|             LaggedStart(ShowCreation, arrows, run_time = 3),
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
| class RemovedConnectionBetweenLedgerAndCash(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         ledger = Rectangle(
 | |
|             height = 2, width = 1.5,
 | |
|             color = WHITE
 | |
|         )
 | |
|         ledger_name = TextMobject("Ledger")
 | |
|         ledger_name.set_width(ledger.get_width() - MED_SMALL_BUFF)
 | |
|         ledger_name.next_to(ledger.get_top(), DOWN)
 | |
|         ledger.add(ledger_name)
 | |
| 
 | |
|         arrow = TexMobject("\\leftrightarrow")
 | |
|         cash = TexMobject("\\$\\$\\$")
 | |
|         cash.set_color(GREEN)
 | |
|         arrow.next_to(ledger, RIGHT)
 | |
|         cash.next_to(arrow, RIGHT)
 | |
|         group = VGroup(ledger, arrow, cash)
 | |
|         group.next_to(self.teacher, UP+LEFT)
 | |
| 
 | |
|         self.add(group)
 | |
|         self.play(
 | |
|             Animation(group),
 | |
|             self.teacher.change, "raise_right_hand"
 | |
|         )
 | |
|         self.play(
 | |
|             arrow.shift, 2*UP,
 | |
|             arrow.set_fill, None, 0
 | |
|         )
 | |
|         self.play(
 | |
|             ledger.shift, LEFT, 
 | |
|             cash.shift, RIGHT
 | |
|         )
 | |
|         self.change_student_modes(
 | |
|             *["pondering"]*3,
 | |
|             look_at_arg = ledger,
 | |
|             added_anims = [self.teacher.change, "happy"]
 | |
|         )
 | |
|         self.wait(3)
 | |
| 
 | |
| class RenameToLedgerDollars(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "payments" : [
 | |
|             ("Alice", "Bob", 20),
 | |
|             ("Charlie", "You", 80),
 | |
|             ("Bob", "Charlie", 60),
 | |
|             ("Bob", "Alice", 30),
 | |
|             ("Alice", "You", 100),
 | |
|         ],
 | |
|         "enumerate_lines" : True,
 | |
|         "line_number_color" : WHITE,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add(self.get_ledger())
 | |
|         self.add_bubble()
 | |
|         self.jump_in_to_middle()
 | |
|         self.add_payments_in_dollars()
 | |
|         self.rewrite_as_ledger_dollars()
 | |
| 
 | |
|     def add_bubble(self):
 | |
|         randy = self.pi_creature
 | |
|         bubble = SpeechBubble(direction = RIGHT)
 | |
|         bubble.write("Who needs \\\\ cash?")
 | |
|         bubble.resize_to_content()
 | |
|         bubble.add(bubble.content)
 | |
|         bubble.pin_to(randy)
 | |
|         bubble.shift(MED_LARGE_BUFF*RIGHT)
 | |
|         self.bubble = bubble
 | |
| 
 | |
|         self.add(randy, bubble)
 | |
|         self.add_foreground_mobject(bubble)
 | |
| 
 | |
|     def jump_in_to_middle(self):
 | |
|         h_line = self.ledger.content[0]
 | |
|         dots = TexMobject("\\vdots")
 | |
|         dots.next_to(h_line.get_left(), DOWN)
 | |
|         h_line.add(dots)
 | |
|         self.add(h_line)
 | |
|         point = VectorizedPoint(h_line.get_corner(DOWN+LEFT))
 | |
|         point.shift(MED_SMALL_BUFF*LEFT)
 | |
|         self.ledger.content.add(*[
 | |
|             point.copy()
 | |
|             for x in range(103)
 | |
|         ])
 | |
| 
 | |
|     def add_payments_in_dollars(self):
 | |
|         lines = VGroup(*[
 | |
|             self.add_payment_line_to_ledger(*payment)
 | |
|             for payment in self.payments
 | |
|         ])
 | |
| 
 | |
|         self.play(LaggedStart(
 | |
|             FadeIn, lines,
 | |
|             run_time = 4,
 | |
|             lag_ratio = 0.3
 | |
|         ))
 | |
|         self.wait()
 | |
| 
 | |
|         self.payment_lines = lines
 | |
| 
 | |
|     def rewrite_as_ledger_dollars(self):
 | |
|         curr_lines = self.payment_lines
 | |
|         amounts = VGroup(*[line[4] for line in curr_lines])
 | |
|         amounts.target = VGroup()
 | |
|         for amount in amounts:
 | |
|             dollar_sign = amount[0]
 | |
|             amount.remove(dollar_sign)
 | |
|             amount.add(dollar_sign)
 | |
|             tex_string = amount.get_tex_string()
 | |
|             ld = TextMobject(tex_string[2:] + " LD")
 | |
|             ld.set_color(YELLOW)
 | |
|             ld.scale(0.8)
 | |
|             ld.move_to(amount, LEFT)
 | |
|             amounts.target.add(ld)
 | |
| 
 | |
|         ledger_dollars = TextMobject("Ledger Dollars \\\\ ``LD'' ")
 | |
|         ledger_dollars.set_color(YELLOW)
 | |
|         ledger_dollars.next_to(self.ledger, RIGHT)
 | |
| 
 | |
|         self.play(
 | |
|             Write(ledger_dollars),
 | |
|             FadeOut(self.bubble),
 | |
|             self.pi_creature.change, "thinking", ledger_dollars
 | |
|         )
 | |
|         self.play(MoveToTarget(amounts))
 | |
|         self.wait(2)
 | |
| 
 | |
| 
 | |
|     ###
 | |
| 
 | |
|     def create_pi_creatures(self):
 | |
|         LedgerScene.create_pi_creatures(self)
 | |
|         randy = Randolph(mode = "shruggie").flip()
 | |
|         randy.to_corner(DOWN+RIGHT)
 | |
|         return VGroup(randy)
 | |
| 
 | |
| class ExchangeCashForLedgerDollars(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "sign_transactions" : True,
 | |
|         "denomination" : "LD",
 | |
|         "ledger_width" : 7.5,
 | |
|         "ledger_height" : 6,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_ledger_and_network()
 | |
|         self.add_ellipsis()
 | |
|         self.add_title()
 | |
|         self.give_ten_dollar_bill()
 | |
|         self.add_bob_pays_alice_line()
 | |
|         self.everyone_thinks()
 | |
| 
 | |
|     def add_title(self):
 | |
|         self.ledger.shift(DOWN)
 | |
|         title = TextMobject(
 | |
|             "Exchange", "LD", "for", "\\$\\$\\$"
 | |
|         )
 | |
|         title.set_color_by_tex("LD", YELLOW)
 | |
|         title.set_color_by_tex("\\$", GREEN)
 | |
|         title.scale(1.3)
 | |
|         title.to_edge(UP).shift(LEFT)
 | |
| 
 | |
|         self.play(Write(title))
 | |
|         self.wait()
 | |
| 
 | |
|     def give_ten_dollar_bill(self):
 | |
|         bill = TenDollarBill()
 | |
|         bill.next_to(self.alice.get_corner(UP+RIGHT), UP)
 | |
|         bill.generate_target()
 | |
|         bill.target.next_to(self.bob.get_corner(UP+LEFT), UP)
 | |
| 
 | |
|         arrow = Arrow(bill, bill.target, color = GREEN)
 | |
| 
 | |
|         small_bill = bill.copy()
 | |
|         small_bill.scale(0.01, about_point = bill.get_bottom())
 | |
| 
 | |
|         self.play(
 | |
|             ReplacementTransform(small_bill, bill),
 | |
|             self.alice.change, "raise_right_hand"
 | |
|         )
 | |
|         self.play(ShowCreation(arrow))
 | |
|         self.play(MoveToTarget(bill))
 | |
|         self.play(self.bob.change, "happy", bill)
 | |
|         self.wait()
 | |
| 
 | |
|     def add_bob_pays_alice_line(self):
 | |
|         line = self.add_payment_line_to_ledger(
 | |
|             "Bob", "Alice", 10
 | |
|         )
 | |
|         line.save_state()
 | |
|         line.scale(0.01)
 | |
|         line.move_to(self.bob.get_corner(UP+LEFT))
 | |
|         self.play(self.bob.change, "raise_right_hand", line)
 | |
|         self.play(line.restore, run_time = 2)
 | |
|         self.wait()
 | |
| 
 | |
|     def everyone_thinks(self):
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change, "thinking", self.ledger)
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
|         self.wait(4)
 | |
| 
 | |
| class BitcoinIsALedger(Scene):
 | |
|     def construct(self):
 | |
|         self.add_btc_to_ledger()
 | |
|         self.add_currency_to_tx_history()
 | |
| 
 | |
|     def add_btc_to_ledger(self):
 | |
|         logo = BitcoinLogo()
 | |
|         ledger = self.get_ledger()
 | |
|         arrow = TexMobject("\\Leftrightarrow")
 | |
|         group = VGroup(logo, arrow, ledger)
 | |
|         group.arrange_submobjects(RIGHT)
 | |
| 
 | |
|         self.play(DrawBorderThenFill(logo))
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             Write(arrow),
 | |
|             Write(ledger)
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
|         self.btc_to_ledger = group
 | |
| 
 | |
|     def add_currency_to_tx_history(self):
 | |
|         equation = TextMobject(
 | |
|             "Currency", "=", "Transaction history"
 | |
|         )
 | |
|         equation.set_color_by_tex("Currency", BITCOIN_COLOR)
 | |
|         equation.shift(
 | |
|             self.btc_to_ledger[1].get_center() - \
 | |
|             equation[1].get_center() + 2*UP
 | |
|         )
 | |
| 
 | |
|         for part in reversed(equation):
 | |
|             self.play(FadeIn(part))
 | |
|         self.wait()
 | |
| 
 | |
|     def get_ledger(self):
 | |
|         rect = Rectangle(height = 2, width = 1.5)
 | |
|         title = TextMobject("Ledger")
 | |
|         title.set_width(0.8*rect.get_width())
 | |
|         title.next_to(rect.get_top(), DOWN, SMALL_BUFF)
 | |
| 
 | |
|         lines = VGroup(*[
 | |
|             Line(LEFT, RIGHT)
 | |
|             for x in range(8)
 | |
|         ])
 | |
|         lines.arrange_submobjects(DOWN, buff = SMALL_BUFF)
 | |
|         lines.stretch_to_fit_width(title.get_width())
 | |
|         lines.next_to(title, DOWN)
 | |
|         return VGroup(rect, title, lines)
 | |
| 
 | |
| class BigDifferenceBetweenLDAndCryptocurrencies(Scene):
 | |
|     def construct(self):
 | |
|         ld = TextMobject("LD").scale(1.5).set_color(YELLOW)
 | |
|         btc = BitcoinLogo()
 | |
|         eth = EthereumLogo()
 | |
|         ltc = LitecoinLogo()
 | |
|         logos = VGroup(ltc, eth, ld, btc)
 | |
|         cryptos = VGroup(btc, eth, ltc)
 | |
|         for logo in cryptos:
 | |
|             logo.set_height(1)
 | |
|         vects = compass_directions(4, DOWN+LEFT)
 | |
|         for logo, vect in zip(logos, vects):
 | |
|             logo.move_to(0.75*vect)
 | |
| 
 | |
|         centralized = TextMobject("Centralized")
 | |
|         decentralized = TextMobject("Decentralized")
 | |
|         words = VGroup(centralized, decentralized)
 | |
|         words.scale(1.5)
 | |
|         words.to_edge(UP)
 | |
|         for word, vect in zip(words, [RIGHT, LEFT]):
 | |
|             word.shift(FRAME_X_RADIUS*vect/2)
 | |
| 
 | |
|         self.add(logos)
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             cryptos.next_to, decentralized, DOWN, LARGE_BUFF,
 | |
|             ld.next_to, centralized, DOWN, LARGE_BUFF,
 | |
|         )
 | |
|         self.play(*list(map(Write, words)))
 | |
|         self.wait(2)
 | |
| 
 | |
| class DistributedLedgerScene(LedgerScene):
 | |
|     def get_large_network(self):
 | |
|         network = self.get_network()
 | |
|         network.set_height(FRAME_HEIGHT - LARGE_BUFF)
 | |
|         network.center()
 | |
|         for pi in self.pi_creatures:
 | |
|             pi.label.scale(0.8, about_point = pi.get_bottom())
 | |
|         return network
 | |
| 
 | |
|     def get_distributed_ledgers(self):
 | |
|         ledger = self.get_ledger()
 | |
|         title = ledger[1]
 | |
|         h_line = ledger.content
 | |
|         title.set_width(0.7*ledger.get_width())
 | |
|         title.next_to(ledger.get_top(), DOWN, MED_LARGE_BUFF)
 | |
|         h_line.next_to(title, DOWN)
 | |
|         added_lines = VGroup(*[h_line.copy() for x in range(5)])
 | |
|         added_lines.arrange_submobjects(DOWN, buff = MED_LARGE_BUFF)
 | |
|         added_lines.next_to(h_line, DOWN, MED_LARGE_BUFF)
 | |
|         ledger.content.add(added_lines)
 | |
| 
 | |
|         ledgers = VGroup()
 | |
|         for pi in self.pi_creatures:
 | |
|             pi.ledger = ledger.copy()
 | |
|             pi.ledger.set_height(pi.get_height())
 | |
|             pi.ledger[0].set_color(pi.get_color())
 | |
|             vect = pi.get_center()-self.pi_creatures.get_center()
 | |
|             x_vect = vect[0]*RIGHT
 | |
|             pi.ledger.next_to(pi, x_vect, SMALL_BUFF)
 | |
|             ledgers.add(pi.ledger)
 | |
|         return ledgers
 | |
| 
 | |
|     def add_large_network_and_distributed_ledger(self):
 | |
|         self.add(self.get_large_network())
 | |
|         self.add(self.get_distributed_ledgers())        
 | |
| 
 | |
| class TransitionToDistributedLedger(DistributedLedgerScene):
 | |
|     CONFIG = {
 | |
|         "sign_transactions" : True,
 | |
|         "ledger_width" : 7.5,
 | |
|         "ledger_height" : 6,
 | |
|         "enumerate_lines" : True,
 | |
|         "denomination" : "LD",
 | |
|         "line_number_color" : WHITE,
 | |
|         "ledger_line_height" : 0.35,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_ledger_and_network()
 | |
|         self.ledger.shift(DOWN)
 | |
|         self.add_ellipsis()
 | |
| 
 | |
|         self.ask_where_is_ledger()
 | |
|         self.add_various_payements()
 | |
|         self.ask_who_controls_ledger()
 | |
|         self.distribute_ledger()
 | |
|         self.broadcast_transaction()
 | |
|         self.ask_about_ledger_consistency()
 | |
| 
 | |
|     def ask_where_is_ledger(self):
 | |
|         question = TextMobject("Where", "is", "this?!")
 | |
|         question.set_color(RED)
 | |
|         question.scale(1.5)
 | |
|         question.next_to(self.ledger, UP)
 | |
| 
 | |
|         self.play(Write(question))
 | |
|         self.wait()
 | |
| 
 | |
|         self.question = question
 | |
| 
 | |
|     def add_various_payements(self):
 | |
|         payments = VGroup(*[
 | |
|             self.add_payment_line_to_ledger(*payment)
 | |
|             for payment in [
 | |
|                 ("Alice", "Bob", 20),
 | |
|                 ("Charlie", "You", 100),
 | |
|                 ("You", "Alice", 50),
 | |
|                 ("Bob", "You", 30),
 | |
|             ]
 | |
|         ])
 | |
| 
 | |
|         for payment in payments:
 | |
|             self.play(LaggedStart(FadeIn, payment, run_time = 1))
 | |
|             self.wait()
 | |
| 
 | |
|     def ask_who_controls_ledger(self):
 | |
|         new_question = TextMobject("Who", "controls", "this?!")
 | |
|         new_question.scale(1.3)
 | |
|         new_question.move_to(self.question)
 | |
|         new_question.set_color(RED)
 | |
| 
 | |
|         self.play(Transform(self.question, new_question))
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change, "confused", new_question)
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
|         self.wait(2)
 | |
| 
 | |
|     def distribute_ledger(self):
 | |
|         ledger = self.ledger
 | |
|         self.ledger_width = 6
 | |
|         self.ledger_height = 7
 | |
|         distribute_ledgers = self.get_distributed_ledgers()
 | |
|         group = VGroup(self.network, distribute_ledgers)
 | |
| 
 | |
|         self.play(FadeOut(self.question))
 | |
|         self.play(ReplacementTransform(
 | |
|             VGroup(ledger), distribute_ledgers
 | |
|         ))
 | |
|         self.play(*[
 | |
|             ApplyMethod(pi.change, "pondering", pi.ledger)
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
|         self.play(
 | |
|             group.set_height, FRAME_HEIGHT - 2,
 | |
|             group.center
 | |
|         )
 | |
|         self.wait(2)
 | |
| 
 | |
|     def broadcast_transaction(self):
 | |
|         payment = TextMobject(
 | |
|             "Alice", "pays", "Bob", "100 LD"
 | |
|         )
 | |
|         payment.set_color_by_tex("Alice", self.alice.get_color())
 | |
|         payment.set_color_by_tex("Bob", self.bob.get_color())
 | |
|         payment.set_color_by_tex("LD", YELLOW)
 | |
|         payment.scale(0.75)
 | |
|         payment.add_background_rectangle()
 | |
|         payment_copies = VGroup(*[
 | |
|             payment.copy().next_to(pi, UP)
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
|         payment = payment_copies[0]
 | |
| 
 | |
|         self.play(
 | |
|             self.alice.change, "raise_right_hand", payment,
 | |
|             Write(payment, run_time = 2)
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             ReplacementTransform(
 | |
|                 VGroup(payment), payment_copies, 
 | |
|                 run_time = 3,
 | |
|                 rate_func = squish_rate_func(smooth, 0.5, 1)
 | |
|             ),
 | |
|             Broadcast(self.alice.get_corner(UP+RIGHT)),
 | |
|             self.alice.change, "happy"
 | |
|         )
 | |
|         self.wait()
 | |
|         pairs = list(zip(payment_copies, self.pi_creatures))
 | |
|         Scene.play(self, *it.chain(*[
 | |
|             [
 | |
|                 pi.look_at, pi.ledger[-1],
 | |
|                 line.scale, 0.2,
 | |
|                 line.next_to, pi.ledger[-1], DOWN, SMALL_BUFF,
 | |
|             ]
 | |
|             for line, pi in pairs
 | |
|         ]))
 | |
|         self.wait(3)
 | |
| 
 | |
|         for line, pi in pairs:
 | |
|             pi.ledger.add(line)
 | |
| 
 | |
|     def ask_about_ledger_consistency(self):
 | |
|         ledgers = VGroup(*[
 | |
|             pi.ledger
 | |
|             for pi in self.pi_creatures
 | |
|         ])
 | |
| 
 | |
|         self.play(
 | |
|             FadeOut(self.network),
 | |
|             ledgers.scale, 2,
 | |
|             ledgers.arrange_submobjects, RIGHT,
 | |
|             ledgers.space_out_submobjects,
 | |
|         )
 | |
| 
 | |
|         question = TextMobject("Are these the same?")
 | |
|         question.scale(1.5)
 | |
|         question.to_edge(UP)
 | |
|         arrows = VGroup(*[
 | |
|             Arrow(question.get_bottom(), ledger.get_top())
 | |
|             for ledger in ledgers
 | |
|         ])
 | |
| 
 | |
|         self.play(*list(map(ShowCreation, arrows)))
 | |
|         self.play(Write(question))
 | |
|         self.wait()
 | |
| 
 | |
| class BobDoubtsBroadcastTransaction(DistributedLedgerScene):
 | |
|     def construct(self):
 | |
|         self.setup_bob_and_charlie()
 | |
|         self.bob_receives_transaction()
 | |
|         self.bob_tries_to_pay_charlie()
 | |
| 
 | |
|     def setup_bob_and_charlie(self):
 | |
|         bob, charlie = self.bob, self.charlie
 | |
|         self.pi_creatures = VGroup(bob, charlie)
 | |
|         for pi in self.pi_creatures:
 | |
|             pi.flip()
 | |
|         self.pi_creatures.scale(2)
 | |
|         self.pi_creatures.arrange_submobjects(RIGHT, buff = 5)
 | |
| 
 | |
|         for name in "bob", "charlie":
 | |
|             label = TextMobject(name.capitalize())
 | |
|             pi = getattr(self, name)
 | |
|             label.next_to(pi, DOWN)
 | |
|             pi.label = label
 | |
|         bob.make_eye_contact(charlie)
 | |
| 
 | |
|         self.get_distributed_ledgers()
 | |
| 
 | |
|         self.add(bob, bob.label, bob.ledger)
 | |
| 
 | |
|     def bob_receives_transaction(self):
 | |
|         bob, charlie = self.bob, self.charlie
 | |
|         corner = FRAME_Y_RADIUS*UP + FRAME_X_RADIUS*LEFT
 | |
| 
 | |
|         payment = TextMobject(
 | |
|             "Alice", "pays", "Bob", "10 LD"
 | |
|         )
 | |
|         payment.set_color_by_tex("Alice", self.alice.get_color())
 | |
|         payment.set_color_by_tex("Bob", self.bob.get_color())
 | |
|         payment.set_color_by_tex("LD", YELLOW)
 | |
|         payment.next_to(corner, UP+LEFT)
 | |
| 
 | |
|         self.play(
 | |
|             Broadcast(corner),
 | |
|             ApplyMethod(
 | |
|                 payment.next_to, bob, UP,
 | |
|                 run_time = 3,
 | |
|                 rate_func = squish_rate_func(smooth, 0.3, 1)
 | |
|             ),
 | |
|         )
 | |
|         self.play(bob.look_at, payment)
 | |
|         self.play(
 | |
|             payment.scale, 0.3,
 | |
|             payment.next_to, bob.ledger[-1], DOWN, SMALL_BUFF
 | |
|         )
 | |
| 
 | |
|     def bob_tries_to_pay_charlie(self):
 | |
|         bob, charlie = self.bob, self.charlie
 | |
|         chralie_group = VGroup(
 | |
|             charlie, charlie.label, charlie.ledger
 | |
|         )
 | |
| 
 | |
|         self.play(
 | |
|             PiCreatureSays(
 | |
|                 bob, "Did you hear that?",
 | |
|                 target_mode = "sassy"
 | |
|             ),
 | |
|             FadeIn(chralie_group)
 | |
|         )
 | |
|         self.play(charlie.change, "maybe", bob.eyes)
 | |
|         self.wait(2)
 | |
| 
 | |
| class YouListeningToBroadcasts(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "denomination" : "LD"
 | |
|     }
 | |
|     def construct(self):
 | |
|         ledger = self.get_ledger()
 | |
|         payments = VGroup(*[
 | |
|             self.add_payment_line_to_ledger(*payment)
 | |
|             for payment in [
 | |
|                 ("Alice", "You", 20),
 | |
|                 ("Bob", "You", 50),
 | |
|                 ("Charlie", "You", 30),
 | |
|             ]
 | |
|         ])
 | |
|         self.remove(self.ledger)
 | |
|         corners = [
 | |
|             FRAME_X_RADIUS*RIGHT*u1 + FRAME_Y_RADIUS*UP*u2
 | |
|             for u1, u2 in (-1, 1), (1, 1), (-1, -1)
 | |
|         ]
 | |
|         you = self.you
 | |
|         you.scale(2)
 | |
|         you.center()
 | |
| 
 | |
|         self.add(you)
 | |
|         for payment, corner in zip(payments, corners):
 | |
|             vect = corner/np.linalg.norm(corner)
 | |
|             payment.next_to(corner, vect)
 | |
|             self.play(
 | |
|                 Broadcast(corner),
 | |
|                 ApplyMethod(
 | |
|                     payment.next_to, you, vect,
 | |
|                     run_time = 3,
 | |
|                     rate_func = squish_rate_func(smooth, 0.3, 1)
 | |
|                 ),
 | |
|                 you.change_mode, "pondering"
 | |
|             )
 | |
|         self.wait()
 | |
| 
 | |
| class AskWhatToAddToProtocol(InitialProtocol):
 | |
|     def construct(self):
 | |
|         self.add_title()
 | |
|         items = VGroup(*list(map(self.get_new_item, [
 | |
|             "Broadcast transactions",
 | |
|             "Only accept signed transactions",
 | |
|             "No overspending",
 | |
|         ] + [""]*6)))
 | |
|         brace = Brace(VGroup(*items[3:]), LEFT)
 | |
|         question = TextMobject("What to \\\\ add here?")
 | |
|         question.set_color(RED)
 | |
|         question.scale(1.5)
 | |
|         brace.set_color(RED)
 | |
|         question.next_to(brace, LEFT)
 | |
| 
 | |
|         self.add(*items[:3])
 | |
|         self.play(GrowFromCenter(brace))
 | |
|         self.play(Write(question))
 | |
|         self.wait()
 | |
| 
 | |
| class TrustComputationalWork(DistributedLedgerScene):
 | |
|     def construct(self):
 | |
|         self.add_ledger()
 | |
|         self.show_work()
 | |
| 
 | |
|     def add_ledger(self):
 | |
|         ledgers = self.get_distributed_ledgers()
 | |
|         ledger = ledgers[0]
 | |
|         ledger.scale(3)
 | |
|         ledger[1].scale_in_place(2./3)
 | |
|         ledger.center().to_edge(UP).shift(4*LEFT)
 | |
|         plus = TexMobject("+")
 | |
|         plus.next_to(ledger, RIGHT)
 | |
| 
 | |
|         self.add(ledger, plus)
 | |
|         self.ledger = ledger
 | |
|         self.plus = plus
 | |
| 
 | |
|     def show_work(self):
 | |
|         zeros = TexMobject("0"*32)
 | |
|         zeros.next_to(self.plus, RIGHT)
 | |
|         brace = Brace(zeros, DOWN)
 | |
|         words = brace.get_text("Computational work")
 | |
|         self.add(brace, words)
 | |
| 
 | |
|         for n in range(2**12):
 | |
|             binary = bin(n)[2:]
 | |
|             for i, bit_str in enumerate(reversed(binary)):
 | |
|                 curr_bit = zeros.submobjects[-i-1]
 | |
|                 new_bit = TexMobject(bit_str)
 | |
|                 new_bit.replace(curr_bit, dim_to_match = 1)
 | |
|                 if bit_str == "1":
 | |
|                     new_bit.set_color(YELLOW)
 | |
|                 zeros.submobjects[-i-1] = new_bit
 | |
|                 self.remove(curr_bit)
 | |
|             self.add(zeros)
 | |
|             self.wait(1./30)
 | |
| 
 | |
| class TrustComputationalWorkSupplement(Scene):
 | |
|     def construct(self):
 | |
|         words = TextMobject(
 | |
|             "Main tool: ", "Cryptographic hash functions"
 | |
|         )
 | |
|         words[1].set_color(YELLOW)
 | |
|         self.add(words[0])
 | |
|         self.play(Write(words[1]))
 | |
|         self.wait()
 | |
| 
 | |
| class FraudIsInfeasible(Scene):
 | |
|     def construct(self):
 | |
|         words = TextMobject(
 | |
|             "Fraud", "$\\Leftrightarrow$",
 | |
|             "Computationally infeasible"
 | |
|         )
 | |
|         words.set_color_by_tex("Fraud", RED)
 | |
|         words.to_edge(UP)
 | |
|         self.play(FadeIn(words[0]))
 | |
|         self.play(FadeIn(words[2]))
 | |
|         self.play(Write(words[1]))
 | |
|         self.wait()
 | |
| 
 | |
| class ThisIsWellIntoTheWeeds(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         idea = TextMobject("Proof of work")
 | |
|         idea.move_to(self.teacher.get_corner(UP+LEFT))
 | |
|         idea.shift(MED_LARGE_BUFF*UP)
 | |
|         idea.save_state()
 | |
|         lightbulb = Lightbulb()
 | |
|         lightbulb.next_to(idea, UP)
 | |
|         idea.shift(DOWN)
 | |
|         idea.set_fill(opacity = 0)
 | |
| 
 | |
|         self.teacher_says(
 | |
|             "We're well into \\\\ the weeds now",
 | |
|             target_mode = "sassy",
 | |
|             added_anims = [
 | |
|                 ApplyMethod(pi.change, mode)
 | |
|                 for pi, mode in zip(self.students, [
 | |
|                     "hooray", "sad", "erm"
 | |
|                 ])
 | |
|             ],
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             idea.restore,
 | |
|             RemovePiCreatureBubble(
 | |
|                 self.teacher, target_mode = "hooray",
 | |
|                 look_at_arg = lightbulb
 | |
|             ),
 | |
|         )
 | |
|         self.change_student_modes(
 | |
|             *["pondering"]*3,
 | |
|             added_anims = [LaggedStart(FadeIn, lightbulb)]
 | |
|         )
 | |
|         self.play(LaggedStart(
 | |
|             ApplyMethod, lightbulb,
 | |
|             lambda b : (b.set_color, YELLOW_A),
 | |
|             rate_func = there_and_back
 | |
|         ))
 | |
|         self.wait(2)
 | |
| 
 | |
| class IntroduceSHA256(Scene):
 | |
|     def construct(self):
 | |
|         self.introduce_evaluation()
 | |
|         self.inverse_function_question()
 | |
|         self.issue_challenge()
 | |
|         self.shift_everything_down()
 | |
|         self.guess_and_check()
 | |
| 
 | |
|     def introduce_evaluation(self):
 | |
|         messages = [
 | |
|             "3Blue1Brown",
 | |
|             "3Blue1Crown",
 | |
|             "Mathologer",
 | |
|             "Infinite Series",
 | |
|             "Numberphile",
 | |
|             "Welch Labs",
 | |
|             "3Blue1Brown",
 | |
|         ]
 | |
|         groups = VGroup()
 | |
|         for message in messages:
 | |
|             lhs = TextMobject(
 | |
|                 "SHA256", "(``", message, "'') =",
 | |
|                 arg_separator = ""
 | |
|             )
 | |
|             lhs.set_color_by_tex(message, BLUE)
 | |
|             digest = sha256_tex_mob(message)
 | |
|             digest.next_to(lhs, RIGHT)
 | |
|             group = VGroup(lhs, digest)
 | |
|             group.to_corner(UP+RIGHT)
 | |
|             group.shift(MED_LARGE_BUFF*DOWN)
 | |
|             groups.add(group)
 | |
| 
 | |
|         group = groups[0]
 | |
|         lhs, digest = group
 | |
|         sha, lp, message, lp = lhs
 | |
|         sha_brace = Brace(sha, UP)
 | |
|         message_brace = Brace(message, DOWN)
 | |
|         digest_brace = Brace(digest, DOWN)
 | |
|         sha_text = sha_brace.get_text("", "Hash function")
 | |
|         sha_text.set_color(YELLOW)
 | |
|         message_text = message_brace.get_text("Message/file")
 | |
|         message_text.set_color(BLUE)
 | |
|         digest_text = digest_brace.get_text("``Hash'' or ``Digest''")
 | |
|         brace_text_pairs = [
 | |
|             (sha_brace, sha_text),
 | |
|             (message_brace, message_text),
 | |
|             (digest_brace, digest_text),
 | |
|         ]
 | |
| 
 | |
|         looks_random = TextMobject("Looks random")
 | |
|         looks_random.set_color(MAROON_B)
 | |
|         looks_random.next_to(digest_text, DOWN)
 | |
| 
 | |
|         self.add(group)
 | |
|         self.remove(digest)
 | |
|         for brace, text in brace_text_pairs:
 | |
|             if brace is digest_brace:
 | |
|                 self.play(LaggedStart(
 | |
|                     FadeIn, digest,
 | |
|                     run_time = 4,
 | |
|                     lag_ratio = 0.05
 | |
|                 ))
 | |
|                 self.wait()
 | |
|             self.play(
 | |
|                 GrowFromCenter(brace),
 | |
|                 Write(text, run_time = 2)
 | |
|             )
 | |
|             self.wait()
 | |
|         self.play(Write(looks_random))
 | |
|         self.wait(2)
 | |
|         for mob in digest, message:
 | |
|             self.play(LaggedStart(
 | |
|                 ApplyMethod, mob,
 | |
|                 lambda m : (m.set_color, YELLOW),
 | |
|                 rate_func = there_and_back,
 | |
|                 run_time = 1
 | |
|             ))
 | |
|         self.wait()
 | |
|         self.play(FadeOut(looks_random))
 | |
| 
 | |
|         new_lhs, new_digest = groups[1]
 | |
|         char = new_lhs[2][-5]
 | |
|         arrow = Arrow(UP, ORIGIN, buff = 0)
 | |
|         arrow.next_to(char, UP)
 | |
|         arrow.set_color(RED)
 | |
|         self.play(ShowCreation(arrow))
 | |
|         for new_group in groups[1:]:
 | |
|             new_lhs, new_digest = new_group
 | |
|             new_message = new_lhs[2]
 | |
|             self.play(
 | |
|                 Transform(lhs, new_lhs),
 | |
|                 message_brace.stretch_to_fit_width, new_message.get_width(),
 | |
|                 message_brace.next_to, new_message, DOWN,
 | |
|                 MaintainPositionRelativeTo(message_text, message_brace),
 | |
|                 MaintainPositionRelativeTo(sha_brace, lhs[0]),
 | |
|                 MaintainPositionRelativeTo(sha_text, sha_brace)
 | |
|             )
 | |
|             self.play(Transform(
 | |
|                 digest, new_digest,
 | |
|                 run_time = 2,
 | |
|                 submobject_mode = "lagged_start",
 | |
|                 path_arc = np.pi/2
 | |
|             ))
 | |
|             if arrow in self.get_mobjects():
 | |
|                 self.wait()
 | |
|                 self.play(FadeOut(arrow))
 | |
|         self.wait()
 | |
| 
 | |
|         new_sha_text = TextMobject(
 | |
|             "Cryptographic", "hash function"
 | |
|         )
 | |
|         new_sha_text.next_to(sha_brace, UP)
 | |
|         new_sha_text.shift_onto_screen()
 | |
|         new_sha_text.set_color(YELLOW)
 | |
|         new_sha_text[0].set_color(GREEN)
 | |
|         self.play(Transform(sha_text, new_sha_text))
 | |
|         self.wait()
 | |
| 
 | |
|         self.lhs = lhs
 | |
|         self.message = message
 | |
|         self.digest = digest
 | |
|         self.digest_text = digest_text
 | |
|         self.message_text = message_text
 | |
| 
 | |
|     def inverse_function_question(self):
 | |
|         arrow = Arrow(3*RIGHT, 3*LEFT, buff = 0)
 | |
|         arrow.set_stroke(width = 8)
 | |
|         arrow.set_color(RED)
 | |
|         everything = VGroup(*self.get_mobjects())
 | |
|         arrow.next_to(everything, DOWN)
 | |
|         words = TextMobject("Inverse is infeasible")
 | |
|         words.set_color(RED)
 | |
|         words.next_to(arrow, DOWN)
 | |
| 
 | |
|         self.play(ShowCreation(arrow))
 | |
|         self.play(Write(words))
 | |
|         self.wait()
 | |
| 
 | |
|     def issue_challenge(self):
 | |
|         desired_output_text = TextMobject("Desired output")
 | |
|         desired_output_text.move_to(self.digest_text)
 | |
|         desired_output_text.set_color(YELLOW)
 | |
|         new_digest = sha256_tex_mob("Challenge")
 | |
|         new_digest.replace(self.digest)
 | |
|         q_marks = TextMobject("???")
 | |
|         q_marks.move_to(self.message_text)
 | |
|         q_marks.set_color(BLUE)
 | |
| 
 | |
|         self.play(
 | |
|             Transform(
 | |
|                 self.digest, new_digest,
 | |
|                 run_time = 2,
 | |
|                 submobject_mode = "lagged_start",
 | |
|                 path_arc = np.pi/2
 | |
|             ),
 | |
|             Transform(self.digest_text, desired_output_text)
 | |
|         )
 | |
|         self.play(
 | |
|             FadeOut(self.message),
 | |
|             Transform(self.message_text, q_marks)
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
|     def shift_everything_down(self):
 | |
|         everything = VGroup(*self.get_top_level_mobjects())
 | |
|         self.play(
 | |
|             everything.scale, 0.85,
 | |
|             everything.to_edge, DOWN
 | |
|         )
 | |
| 
 | |
|     def guess_and_check(self):
 | |
|         groups = VGroup()
 | |
|         for x in range(32):
 | |
|             message = "Guess \\#%d"%x
 | |
|             lhs = TextMobject(
 | |
|                 "SHA256(``", message, "'') = ",
 | |
|                 arg_separator = ""
 | |
|             )
 | |
|             lhs.set_color_by_tex("Guess", BLUE)
 | |
|             digest = sha256_tex_mob(message)
 | |
|             digest.next_to(lhs, RIGHT)
 | |
|             group = VGroup(lhs, digest)
 | |
|             group.scale(0.85)
 | |
|             group.next_to(self.digest, UP, aligned_edge = RIGHT)
 | |
|             group.to_edge(UP)
 | |
|             groups.add(group)
 | |
| 
 | |
|         group = groups[0]
 | |
|         self.play(FadeIn(group))
 | |
|         for new_group in groups[1:]:
 | |
|             self.play(Transform(
 | |
|                 group[0], new_group[0],
 | |
|                 run_time = 0.5,
 | |
|             ))
 | |
|             self.play(Transform(
 | |
|                 group[1], new_group[1],
 | |
|                 run_time = 1,
 | |
|                 submobject_mode = "lagged_start"
 | |
|             ))
 | |
| 
 | |
| class PonderScematic(Scene):
 | |
|     def construct(self):
 | |
|         randy = Randolph()
 | |
|         randy.to_corner(DOWN+LEFT)
 | |
|         self.play(randy.change, "confused", ORIGIN)
 | |
|         for x in range(3):
 | |
|             self.play(Blink(randy))
 | |
|             self.wait(2)
 | |
| 
 | |
| class ViewingSLLCertificate(ExternallyAnimatedScene):
 | |
|     pass
 | |
| 
 | |
| class SHA256ToProofOfWork(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         sha = TextMobject("SHA256")
 | |
|         proof = TextMobject("Proof of work")
 | |
|         arrow = Arrow(LEFT, RIGHT)
 | |
|         group = VGroup(sha, arrow, proof)
 | |
|         group.arrange_submobjects(RIGHT)
 | |
|         group.next_to(self.teacher, UP, buff = LARGE_BUFF)
 | |
|         group.to_edge(RIGHT, buff = LARGE_BUFF)
 | |
| 
 | |
|         self.play(
 | |
|             Write(sha, run_time = 1),
 | |
|             self.teacher.change, "raise_right_hand"
 | |
|         )
 | |
|         self.play(ShowCreation(arrow))
 | |
|         self.play(Write(proof, run_time = 1))
 | |
|         self.wait(3)
 | |
| 
 | |
| class IntroduceNonceOnTrasactions(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "denomination" : "LD",
 | |
|         "ledger_width" : 5,
 | |
|         "ledger_line_height" : 0.3,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add(self.get_ledger())
 | |
|         self.hash_with_nonce()
 | |
|         self.write_probability()
 | |
|         self.guess_and_check()
 | |
|         self.name_proof_of_work()
 | |
|         self.change_ledger()
 | |
|         self.guess_and_check()
 | |
| 
 | |
|     def hash_with_nonce(self):
 | |
|         ledger = self.ledger
 | |
|         self.add(*[
 | |
|             self.add_payment_line_to_ledger(*payment)
 | |
|             for payment in [
 | |
|                 ("Alice", "Bob", 20),
 | |
|                 ("Alice", "You", 30),
 | |
|                 ("Charlie", "You", 100),
 | |
|             ]
 | |
|         ])
 | |
| 
 | |
|         nonce = TexMobject(str(2**30 + hash("Hey there")%(2**15)))
 | |
|         nonce.next_to(ledger, RIGHT, LARGE_BUFF)
 | |
|         nonce.set_color(GREEN_C)
 | |
|         nonce_brace = Brace(nonce, DOWN)
 | |
|         special_word = nonce_brace.get_text("Special number")
 | |
|         arrow = Arrow(LEFT, RIGHT, buff = 0)
 | |
|         arrow.next_to(ledger, RIGHT)
 | |
|         arrow.shift(MED_LARGE_BUFF*DOWN)
 | |
|         sha = TextMobject("SHA256")
 | |
|         sha.next_to(arrow, UP)
 | |
|         digest = sha256_tex_mob(
 | |
|             """Man, you're reading this deeply into
 | |
|             the code behind videos?  I'm touched,
 | |
|             really touched.  Keeping loving math, my
 | |
|             friend. """,
 | |
|             n_forced_start_zeros = 30,
 | |
|         )
 | |
|         digest.next_to(arrow, RIGHT)
 | |
|         zeros = VGroup(*digest[:30])
 | |
|         zeros_brace = Brace(zeros, UP)
 | |
|         zeros_words = zeros_brace.get_text("30 zeros")
 | |
| 
 | |
|         self.play(LaggedStart(
 | |
|             FadeIn, VGroup(special_word, nonce_brace, nonce)
 | |
|         ))
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             nonce.next_to, ledger.content, DOWN, MED_SMALL_BUFF, LEFT,
 | |
|             FadeOut(special_word),
 | |
|             FadeOut(nonce_brace)
 | |
|         )
 | |
|         ledger.content.add(nonce)
 | |
|         decomposed_ledger = VGroup(*[m for m in ledger.family_members_with_points() if not m.is_subpath])
 | |
|         self.play(
 | |
|             ShowCreation(arrow),
 | |
|             FadeIn(sha)
 | |
|         )
 | |
|         self.play(LaggedStart(
 | |
|             ApplyMethod, decomposed_ledger,
 | |
|             lambda m : (m.set_color, YELLOW),
 | |
|             rate_func = there_and_back
 | |
|         ))
 | |
|         point = VectorizedPoint(sha.get_center())
 | |
|         point.set_fill(opacity = 1)
 | |
|         self.play(LaggedStart(
 | |
|             Transform, decomposed_ledger.copy(),
 | |
|             lambda m : (m, point),
 | |
|             run_time = 1
 | |
|         ))
 | |
|         bit_iter = iter(digest)
 | |
|         self.play(LaggedStart(
 | |
|             ReplacementTransform, 
 | |
|             VGroup(*[point.copy() for x in range(256)]),
 | |
|             lambda m : (m, next(bit_iter)),
 | |
|         ))
 | |
|         self.remove(*self.get_mobjects_from_last_animation())
 | |
|         self.add(digest)
 | |
|         self.play(
 | |
|             GrowFromCenter(zeros_brace),
 | |
|             Write(zeros_words, run_time = 1)
 | |
|         )
 | |
|         self.play(LaggedStart(
 | |
|             ApplyMethod, zeros,
 | |
|             lambda m : (m.set_color, YELLOW)
 | |
|         ))
 | |
|         self.wait(2)
 | |
| 
 | |
|         self.nonce = nonce
 | |
|         self.digest = digest
 | |
|         self.zeros_brace = zeros_brace
 | |
|         self.zeros_words = zeros_words
 | |
| 
 | |
|     def write_probability(self):
 | |
|         probability = TextMobject(
 | |
|             "Probability: $\\frac{1}{2^{30}}$",
 | |
|             "$\\approx \\frac{1}{1{,}000{,}000{,}000}$",
 | |
|         )
 | |
|         probability.next_to(self.zeros_words, UP, MED_LARGE_BUFF)
 | |
| 
 | |
|         self.play(FadeIn(probability[0]))
 | |
|         self.wait()
 | |
|         self.play(Write(probability[1], run_time = 2))
 | |
|         self.wait(2)
 | |
| 
 | |
|     def guess_and_check(self):
 | |
|         q_mark = TexMobject("?")
 | |
|         q_mark.set_color(RED)
 | |
|         q_mark.next_to(self.zeros_words, RIGHT, SMALL_BUFF)
 | |
| 
 | |
|         self.digest.save_state()
 | |
|         self.nonce.save_state()
 | |
| 
 | |
|         self.play(FadeIn(q_mark))
 | |
|         for x in range(1, 13):
 | |
|             nonce = TexMobject(str(x))
 | |
|             nonce.move_to(self.nonce)
 | |
|             nonce.set_color(GREEN_C)
 | |
|             digest = sha256_tex_mob(str(x))
 | |
|             digest.replace(self.digest)
 | |
| 
 | |
|             self.play(Transform(
 | |
|                 self.nonce, nonce,
 | |
|                 run_time = 1 if x == 1 else 0.3
 | |
|             ))
 | |
|             self.play(Transform(
 | |
|                 self.digest, digest,
 | |
|                 run_time = 1,
 | |
|                 submobject_mode = "lagged_start"
 | |
|             ))
 | |
|         self.wait()
 | |
|         self.play(self.nonce.restore)
 | |
|         self.play(
 | |
|             self.digest.restore, 
 | |
|             submobject_mode = "lagged_start",
 | |
|             run_time = 2
 | |
|         )
 | |
|         self.play(FadeOut(q_mark))
 | |
|         self.wait()
 | |
| 
 | |
|     def name_proof_of_work(self):
 | |
|         words = TextMobject("``Proof of work''")
 | |
|         words.next_to(self.nonce, DOWN, LARGE_BUFF)
 | |
|         words.shift(MED_LARGE_BUFF*RIGHT)
 | |
|         words.set_color(GREEN)
 | |
|         arrow = Arrow(
 | |
|             words.get_top(), self.nonce.get_bottom(),
 | |
|             color = WHITE,
 | |
|             tip_length = 0.15
 | |
|         )
 | |
|         self.play(Write(words, run_time = 2))
 | |
|         self.play(ShowCreation(arrow))
 | |
|         self.wait()
 | |
| 
 | |
|     def change_ledger(self):
 | |
|         amount = self.ledger.content[2][-1]
 | |
|         new_amount = TextMobject("300 LD")
 | |
|         new_amount.set_height(amount.get_height())
 | |
|         new_amount.set_color(amount.get_color())
 | |
|         new_amount.move_to(amount, LEFT)
 | |
| 
 | |
|         new_digest = sha256_tex_mob("Ah shucks")
 | |
|         new_digest.replace(self.digest)
 | |
| 
 | |
|         dot = Dot(amount.get_center())
 | |
|         dot.set_fill(opacity = 0.5)
 | |
| 
 | |
|         self.play(FocusOn(amount))
 | |
|         self.play(Transform(amount, new_amount))
 | |
|         self.play(
 | |
|             dot.move_to, new_digest,
 | |
|             dot.set_fill, None, 0
 | |
|         )
 | |
|         self.play(Transform(
 | |
|             self.digest, new_digest,
 | |
|             submobject_mode = "lagged_start",
 | |
|         ))
 | |
| 
 | |
| class ShowSomeBroadcasting(DistributedLedgerScene):
 | |
|     def construct(self):
 | |
|         self.add_large_network_and_distributed_ledger()
 | |
|         lines = self.network.lines.copy()
 | |
|         lines.add(*[
 | |
|             line.copy().rotate(np.pi)
 | |
|             for line in lines
 | |
|         ])
 | |
| 
 | |
|         point = VectorizedPoint(self.pi_creatures.get_center())
 | |
|         last_pi = None
 | |
|         for pi in self.pi_creatures:
 | |
|             outgoing_lines = []
 | |
|             for line in lines:
 | |
|                 vect = line.get_start() - pi.get_center()
 | |
|                 dist = np.linalg.norm(vect)
 | |
|                 if dist < 2:
 | |
|                     outgoing_lines.append(line)
 | |
|             dots = VGroup()
 | |
|             for line in outgoing_lines:
 | |
|                 dot = Dot(line.get_start())
 | |
|                 dot.set_color(YELLOW)
 | |
|                 dot.generate_target()
 | |
|                 dot.target.move_to(line.get_end())
 | |
|                 for alt_pi in self.pi_creatures:
 | |
|                     vect = line.get_end() - alt_pi.get_center()
 | |
|                     dist = np.linalg.norm(vect)
 | |
|                     if dist < 2:
 | |
|                         dot.ledger = alt_pi.ledger
 | |
|                 dots.add(dot)
 | |
|             self.play(
 | |
|                 Animation(point),
 | |
|                 Broadcast(pi),
 | |
|                 *[
 | |
|                     Succession(
 | |
|                         FadeIn(dot),
 | |
|                         MoveToTarget(dot, run_time = 2),
 | |
|                     )
 | |
|                     for dot in dots
 | |
|                 ]
 | |
|             )
 | |
|             self.play(*it.chain(*[
 | |
|                 [dot.move_to, dot.ledger, dot.set_fill, None, 0]
 | |
|                 for dot in dots
 | |
|             ]))
 | |
| 
 | |
| class IntroduceBlockChain(Scene):
 | |
|     CONFIG = {
 | |
|         "transaction_color" : YELLOW,
 | |
|         "proof_of_work_color" : GREEN,
 | |
|         "prev_hash_color" : BLUE,
 | |
|         "block_width" : 3,
 | |
|         "block_height" : 3.5,
 | |
|         "n_transaction_lines" : 8,
 | |
|         "payment_height_to_block_height" : 0.15,
 | |
|     }
 | |
|     def setup(self):
 | |
|         ls = LedgerScene()
 | |
|         self.names = [
 | |
|             name.capitalize()
 | |
|             for name in ls.get_names()
 | |
|         ]
 | |
|         self.name_colors = [
 | |
|             ls.get_color_from_name(name)
 | |
|             for name in self.names
 | |
|         ]
 | |
| 
 | |
|     def construct(self):
 | |
|         self.divide_ledger_into_blocks()
 | |
|         self.show_proofs_of_work()
 | |
|         self.chain_blocks_together()
 | |
|         self.mess_with_early_block()
 | |
|         self.propagate_hash_change()
 | |
|         self.redo_proof_of_work()
 | |
|         self.write_block_chain()
 | |
| 
 | |
| 
 | |
|     def divide_ledger_into_blocks(self):
 | |
|         blocks = VGroup(*[
 | |
|             self.get_block() for x in range(3)
 | |
|         ])
 | |
|         blocks.arrange_submobjects(RIGHT, buff = 1.5)
 | |
|         blocks.to_edge(UP)
 | |
| 
 | |
|         all_payments = VGroup()
 | |
|         all_proofs_of_work = VGroup()
 | |
|         for block in blocks:
 | |
|             block.remove(block.prev_hash)
 | |
|             all_payments.add(*block.payments)
 | |
|             all_proofs_of_work.add(block.proof_of_work)
 | |
| 
 | |
|         blocks_word = TextMobject("Blocks")
 | |
|         blocks_word.scale(1.5)
 | |
|         blocks_word.shift(2*DOWN)
 | |
|         arrows = VGroup(*[
 | |
|             Arrow(
 | |
|                 blocks_word.get_top(), block.get_bottom(),
 | |
|                 buff = MED_LARGE_BUFF,
 | |
|                 color = WHITE
 | |
|             )
 | |
|             for block in blocks
 | |
|         ])
 | |
| 
 | |
|         self.play(LaggedStart(FadeIn, blocks))
 | |
|         self.play(
 | |
|             Write(blocks_word),
 | |
|             LaggedStart(
 | |
|                 ShowCreation, arrows, 
 | |
|                 run_time = 1,
 | |
|                 lag_factor = 0.6,
 | |
|             )
 | |
|         )
 | |
|         self.wait()
 | |
|         for group in all_payments, all_proofs_of_work:
 | |
|             self.play(LaggedStart(
 | |
|                 Indicate, group,
 | |
|                 rate_func = there_and_back,
 | |
|                 scale_factor = 1.1,
 | |
|             ))
 | |
|         self.play(*list(map(FadeOut, [blocks_word, arrows])))
 | |
| 
 | |
|         self.blocks = blocks
 | |
| 
 | |
|     def show_proofs_of_work(self):
 | |
|         random.seed(0)
 | |
|         blocks = self.blocks
 | |
| 
 | |
|         proofs_of_work = VGroup()
 | |
|         new_proofs_of_work = VGroup()
 | |
|         digests = VGroup()
 | |
|         arrows = VGroup()
 | |
|         sha_words = VGroup()
 | |
|         signatures = VGroup()
 | |
| 
 | |
|         for block in blocks:
 | |
|             proofs_of_work.add(block.proof_of_work)
 | |
|             num_str = str(random.randint(0, 10**12))
 | |
|             number = TexMobject(num_str)
 | |
|             number.set_color(self.proof_of_work_color)
 | |
|             number.replace(block.proof_of_work, dim_to_match = 1)
 | |
|             new_proofs_of_work.add(number)
 | |
| 
 | |
|             digest = sha256_tex_mob(num_str, 60)
 | |
|             digest.scale(0.7)
 | |
|             digest.move_to(block).to_edge(DOWN)
 | |
|             VGroup(*digest[:60]).set_color(YELLOW)
 | |
|             arrow = Arrow(block, digest)
 | |
|             sha = TextMobject("SHA256")
 | |
|             sha.scale(0.7)
 | |
|             point = arrow.get_center()
 | |
|             sha.next_to(point, UP, SMALL_BUFF)
 | |
|             sha.rotate(-np.pi/2, about_point = point)
 | |
|             sha.shift(SMALL_BUFF*(UP+RIGHT))
 | |
|             digests.add(digest)
 | |
|             arrows.add(arrow)
 | |
|             sha_words.add(sha)
 | |
| 
 | |
|             for payment in block.payments[:2]:
 | |
|                 signatures.add(payment[-1])
 | |
| 
 | |
|         proofs_of_work.save_state()
 | |
| 
 | |
|         self.play(Transform(
 | |
|             proofs_of_work, new_proofs_of_work,
 | |
|             submobject_mode = "lagged_start"
 | |
|         ))
 | |
|         self.play(
 | |
|             ShowCreation(arrows),
 | |
|             Write(sha_words),
 | |
|             run_time = 2
 | |
|         )
 | |
|         self.play(Write(digests))
 | |
|         self.wait()
 | |
|         for group in signatures, proofs_of_work:
 | |
|             self.play(LaggedStart(
 | |
|                 Indicate, group,
 | |
|                 run_time = 2,
 | |
|                 rate_func = there_and_back,
 | |
|             ))
 | |
|             self.wait()
 | |
|         self.play(
 | |
|             proofs_of_work.restore,
 | |
|             FadeOut(sha_words)
 | |
|         )
 | |
| 
 | |
|         self.digests = digests
 | |
|         self.sha_arrows = arrows
 | |
| 
 | |
|     def chain_blocks_together(self):
 | |
|         blocks = self.blocks
 | |
|         digests = self.digests
 | |
|         sha_arrows = self.sha_arrows
 | |
|         block_spacing = blocks[1].get_center() - blocks[0].get_center()
 | |
|         prev_hashes = VGroup(*[
 | |
|             block.prev_hash for block in blocks
 | |
|         ])
 | |
| 
 | |
|         prev_hashes.add(
 | |
|             prev_hashes[-1].copy().shift(block_spacing).fade(1)
 | |
|         )
 | |
| 
 | |
|         new_arrows = VGroup()
 | |
|         for block in blocks:
 | |
|             end = np.array([
 | |
|                 block.get_left()[0] + block_spacing[0],
 | |
|                 block.prev_hash.get_center()[1],
 | |
|                 0
 | |
|             ])
 | |
|             arrow = Arrow(end+LEFT, end, buff = SMALL_BUFF)
 | |
|             arrow.points[0] = block.get_right()
 | |
|             arrow.points[1] = block.get_right() + RIGHT
 | |
|             arrow.points[2] = end + LEFT + SMALL_BUFF*UP
 | |
|             new_arrows.add(arrow)
 | |
| 
 | |
|         for i in range(3):
 | |
|             self.play(
 | |
|                 ReplacementTransform(digests[i], prev_hashes[i+1]),
 | |
|                 Transform(sha_arrows[i], new_arrows[i])
 | |
|             )
 | |
|         arrow = new_arrows[0].copy().shift(-block_spacing)
 | |
|         sha_arrows.add_to_back(arrow)
 | |
|         self.play(*list(map(FadeIn, [arrow, prev_hashes[0]])))
 | |
|         self.wait(2)
 | |
| 
 | |
|         self.prev_hashes = prev_hashes
 | |
| 
 | |
|     def mess_with_early_block(self):
 | |
|         blocks = self.blocks
 | |
|         amount = blocks[0].payments[1][3]
 | |
|         new_amount = TextMobject("400 LD")
 | |
|         new_amount.set_height(amount.get_height())
 | |
|         new_amount.set_color(RED)
 | |
|         new_amount.move_to(amount, LEFT)
 | |
| 
 | |
|         self.play(FocusOn(amount))
 | |
|         self.play(Transform(amount, new_amount))
 | |
|         self.wait()
 | |
|         self.play(Swap(*blocks[:2]))
 | |
|         self.wait()
 | |
| 
 | |
|         blocks.submobjects[:2] = blocks.submobjects[1::-1]
 | |
| 
 | |
|     def propagate_hash_change(self):
 | |
|         prev_hashes = self.prev_hashes
 | |
| 
 | |
|         for block, prev_hash in zip(self.blocks, prev_hashes[1:]):
 | |
|             rect = block.rect.copy()
 | |
|             rect.set_stroke(RED, 8)
 | |
|             rect.target = SurroundingRectangle(prev_hash)
 | |
|             rect.target.set_stroke(rect.get_color(), 0)
 | |
|             self.play(ShowCreation(rect))
 | |
|             self.play(
 | |
|                 MoveToTarget(rect),
 | |
|                 prev_hash.set_color, RED
 | |
|             )
 | |
| 
 | |
|     def redo_proof_of_work(self):
 | |
|         proofs_of_work = VGroup(*[
 | |
|             block.proof_of_work for block in self.blocks
 | |
|         ])
 | |
|         hashes = self.prev_hashes[1:]
 | |
| 
 | |
|         self.play(FadeOut(proofs_of_work))
 | |
|         for proof_of_work, prev_hash in zip(proofs_of_work, hashes):
 | |
|             num_pow_group = VGroup(*[
 | |
|                 Integer(random.randint(10**9, 10**10))
 | |
|                 for x in range(50)
 | |
|             ])
 | |
|             num_pow_group.set_color(proof_of_work.get_color())
 | |
|             num_pow_group.set_width(proof_of_work.get_width())
 | |
|             num_pow_group.move_to(proof_of_work)
 | |
|             for num_pow in num_pow_group:
 | |
|                 self.add(num_pow)
 | |
|                 self.wait(1./20)
 | |
|                 prev_hash.set_color(random_bright_color())
 | |
|                 self.remove(num_pow)
 | |
|             self.add(num_pow)
 | |
|             prev_hash.set_color(BLUE)
 | |
| 
 | |
|     def write_block_chain(self):
 | |
|         ledger = TextMobject("Ledger")
 | |
|         ledger.next_to(self.blocks, DOWN, LARGE_BUFF)
 | |
|         cross = Cross(ledger)
 | |
|         block_chain = TextMobject("``Block Chain''")
 | |
|         block_chain.next_to(ledger, DOWN)
 | |
| 
 | |
|         self.play(FadeIn(ledger))
 | |
|         self.play(
 | |
|             ShowCreation(cross),
 | |
|             Write(block_chain)
 | |
|         )
 | |
|         self.wait(2)
 | |
| 
 | |
| 
 | |
|     ######
 | |
| 
 | |
|     def get_block(self):
 | |
|         block = VGroup()
 | |
|         rect = Rectangle(
 | |
|             color = WHITE,
 | |
|             height = self.block_height,
 | |
|             width = self.block_width,
 | |
|         )
 | |
|         h_line1, h_line2 = [
 | |
|             Line(
 | |
|                 rect.get_left(), rect.get_right()
 | |
|             ).shift(0.3*rect.get_height()*vect)
 | |
|             for vect in (UP, DOWN)
 | |
|         ]
 | |
| 
 | |
|         payments = VGroup()
 | |
|         if not hasattr(self, "transaction_counter"):
 | |
|             self.transaction_counter = 0
 | |
|         for x in range(2):
 | |
|             hashes = [
 | |
|                 hash("%d %d"%(seed, self.transaction_counter))
 | |
|                 for seed in range(3)
 | |
|             ]
 | |
|             payment = TextMobject(
 | |
|                 self.names[hashes[0]%3],
 | |
|                 "pays",
 | |
|                 self.names[hashes[1]%4],
 | |
|                 "%d0 LD"%(hashes[2]%9 + 1),
 | |
|             )
 | |
|             payment.set_color_by_tex("LD", YELLOW)
 | |
|             for name, color in zip(self.names, self.name_colors):
 | |
|                 payment.set_color_by_tex(name, color)
 | |
|             signature = TextMobject("$\\langle$ Signature $\\rangle$")
 | |
|             signature.set_color(payment[0].get_color())
 | |
|             signature.next_to(payment, DOWN, SMALL_BUFF)
 | |
|             payment.add(signature)
 | |
| 
 | |
|             factor = self.payment_height_to_block_height
 | |
|             payment.set_height(factor*rect.get_height())
 | |
|             payments.add(payment)
 | |
|             self.transaction_counter += 1
 | |
|         payments.add(TexMobject("\\dots").scale(0.5))
 | |
|         payments.arrange_submobjects(DOWN, buff = MED_SMALL_BUFF)
 | |
|         payments.next_to(h_line1, DOWN)
 | |
| 
 | |
|         proof_of_work = TextMobject("Proof of work")
 | |
|         proof_of_work.set_color(self.proof_of_work_color)
 | |
|         proof_of_work.scale(0.8)
 | |
|         proof_of_work.move_to(
 | |
|             VGroup(h_line2, VectorizedPoint(rect.get_bottom()))
 | |
|         )
 | |
| 
 | |
|         prev_hash = TextMobject("Prev hash")
 | |
|         prev_hash.scale(0.8)
 | |
|         prev_hash.set_color(self.prev_hash_color)
 | |
|         prev_hash.move_to(
 | |
|             VGroup(h_line1, VectorizedPoint(rect.get_top()))
 | |
|         )
 | |
| 
 | |
|         block.rect = rect
 | |
|         block.h_lines = VGroup(h_line1, h_line2)
 | |
|         block.payments = payments
 | |
|         block.proof_of_work = proof_of_work
 | |
|         block.prev_hash = prev_hash
 | |
|         block.digest_mobject_attrs()
 | |
|         return block
 | |
| 
 | |
| class DistributedBlockChainScene(DistributedLedgerScene):
 | |
|     CONFIG = {
 | |
|         "block_height" : 0.5,
 | |
|         "block_width" : 0.5,
 | |
|         "n_blocks" : 3,
 | |
|     }
 | |
|     def get_distributed_ledgers(self):
 | |
|         ledgers = VGroup()
 | |
|         point = self.pi_creatures.get_center()
 | |
|         for pi in self.pi_creatures:
 | |
|             vect = pi.get_center() - point
 | |
|             vect[0] = 0
 | |
|             block_chain = self.get_block_chain()
 | |
|             block_chain.next_to(
 | |
|                 VGroup(pi, pi.label), vect, SMALL_BUFF
 | |
|             )
 | |
|             pi.block_chain = pi.ledger = block_chain
 | |
|             ledgers.add(block_chain)
 | |
|         self.ledgers = self.block_chains = ledgers
 | |
|         return ledgers
 | |
| 
 | |
|     def get_block_chain(self):
 | |
|         blocks = VGroup(*[
 | |
|             self.get_block() 
 | |
|             for x in range(self.n_blocks)
 | |
|         ])
 | |
|         blocks.arrange_submobjects(RIGHT, buff = MED_SMALL_BUFF)
 | |
|         arrows = VGroup()
 | |
| 
 | |
|         for b1, b2 in zip(blocks, blocks[1:]):
 | |
|             arrow = Arrow(
 | |
|                 LEFT, RIGHT,
 | |
|                 preserve_tip_size_when_scaling = False,
 | |
|                 tip_length = 0.15,
 | |
|             )
 | |
|             arrow.set_width(b1.get_width())
 | |
|             target_point = interpolate(
 | |
|                 b2.get_left(), b2.get_corner(UP+LEFT), 0.8
 | |
|             )
 | |
|             arrow.next_to(target_point, LEFT, 0.5*SMALL_BUFF)
 | |
|             arrow.points[0] = b1.get_right()
 | |
|             arrow.points[1] = b2.get_left()
 | |
|             arrow.points[2] = b1.get_corner(UP+RIGHT)
 | |
|             arrow.points[2] += SMALL_BUFF*LEFT
 | |
|             arrows.add(arrow)
 | |
|         block_chain = VGroup(blocks, arrows)
 | |
|         block_chain.blocks = blocks
 | |
|         block_chain.arrows = arrows
 | |
|         return block_chain
 | |
| 
 | |
|     def get_block(self):
 | |
|         block = Rectangle(
 | |
|             color = WHITE,
 | |
|             height = self.block_height,
 | |
|             width = self.block_width,
 | |
|         )
 | |
|         for vect in UP, DOWN:
 | |
|             line = Line(block.get_left(), block.get_right())
 | |
|             line.shift(0.3*block.get_height()*vect)
 | |
|             block.add(line)
 | |
|         return block
 | |
| 
 | |
|     def create_pi_creatures(self):
 | |
|         creatures = DistributedLedgerScene.create_pi_creatures(self)
 | |
|         VGroup(
 | |
|             self.alice, self.alice.label,
 | |
|             self.charlie, self.charlie.label,
 | |
|         ).shift(LEFT)
 | |
|         return creatures
 | |
| 
 | |
| #Out of order
 | |
| class FromBankToDecentralizedSystem(DistributedBlockChainScene):
 | |
|     CONFIG = {
 | |
|         "n_blocks" : 5,
 | |
|         "ledger_height" : 3,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.remove_bank()
 | |
|         self.show_block_chains()
 | |
|         self.add_crypto_terms()
 | |
|         self.set_aside_everything()
 | |
| 
 | |
|     def remove_bank(self):
 | |
|         bank = SVGMobject(
 | |
|             file_name = "bank_building",
 | |
|             color = LIGHT_GREY,
 | |
|             height = 3,
 | |
|         )
 | |
|         cross = Cross(bank)
 | |
|         cross.set_stroke(width = 10)
 | |
|         group = VGroup(bank, cross)
 | |
| 
 | |
|         self.play(LaggedStart(DrawBorderThenFill, bank))
 | |
|         self.play(ShowCreation(cross))
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             group.next_to, FRAME_X_RADIUS*RIGHT, RIGHT,
 | |
|             rate_func = running_start,
 | |
|             path_arc = -np.pi/6,
 | |
|         )
 | |
|         self.wait()
 | |
|         self.remove(group)
 | |
| 
 | |
|     def show_block_chains(self):
 | |
|         creatures = self.pi_creatures
 | |
|         creatures.center()
 | |
|         VGroup(self.charlie, self.you).to_edge(DOWN)
 | |
|         chains = self.get_distributed_ledgers()
 | |
|         for pi, chain in zip(creatures, chains):
 | |
|             pi.scale_in_place(1.5)
 | |
|             pi.shift(0.5*pi.get_center()[0]*RIGHT)
 | |
|             chain.next_to(pi, UP)
 | |
|         center_chain = self.get_block_chain()
 | |
|         center_chain.scale(2)
 | |
|         center_chain.center()
 | |
| 
 | |
|         self.play(LaggedStart(FadeIn, creatures, run_time = 1))
 | |
|         self.play(
 | |
|             LaggedStart(FadeIn, center_chain.blocks, run_time = 1),
 | |
|             ShowCreation(center_chain.arrows),
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             ReplacementTransform(VGroup(center_chain), chains),
 | |
|             *[
 | |
|                 ApplyMethod(pi.change, "pondering", pi.ledger)
 | |
|                 for pi in creatures
 | |
|             ]
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
|     def add_crypto_terms(self):
 | |
|         terms = TextMobject(
 | |
|             "Digital signatures \\\\",
 | |
|             "Cryptographic hash functions",
 | |
|         )
 | |
|         terms.set_color_by_tex("signature", BLUE)
 | |
|         terms.set_color_by_tex("hash", YELLOW)
 | |
|         for term in terms:
 | |
|             self.play(Write(term, run_time = 1))
 | |
|             self.wait()
 | |
|         self.digital_signature = terms[0]
 | |
| 
 | |
|     def set_aside_everything(self):
 | |
|         digital_signature = self.digital_signature
 | |
|         ledger = LedgerScene.get_ledger(self)
 | |
|         LedgerScene.add_payment_line_to_ledger(self, 
 | |
|             "Alice", "Bob", "40",
 | |
|         )
 | |
|         LedgerScene.add_payment_line_to_ledger(self,
 | |
|             "Charlie", "Alice", "60",
 | |
|         )
 | |
|         ledger.next_to(ORIGIN, LEFT)
 | |
| 
 | |
|         self.remove(digital_signature)
 | |
|         everything = VGroup(*self.get_top_level_mobjects())
 | |
| 
 | |
|         self.play(
 | |
|             Animation(digital_signature),
 | |
|             everything.scale, 0.1,
 | |
|             everything.to_corner, DOWN+LEFT,
 | |
|         )
 | |
|         self.play(
 | |
|             Write(ledger),
 | |
|             digital_signature.next_to, ORIGIN, RIGHT
 | |
|         )
 | |
|         self.wait(2)
 | |
| 
 | |
| class IntroduceBlockCreator(DistributedBlockChainScene):
 | |
|     CONFIG = {
 | |
|         "n_block_creators" : 3,
 | |
|         "n_pow_guesses" : 60,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_network()
 | |
|         self.add_block_creators()
 | |
|         self.broadcast_transactions()
 | |
|         self.collect_transactions()
 | |
|         self.find_proof_of_work()
 | |
|         self.add_block_reward()
 | |
|         self.comment_on_block_reward()
 | |
|         self.write_miners()
 | |
|         self.broadcast_block()
 | |
| 
 | |
|     def add_network(self):
 | |
|         network = self.get_large_network()
 | |
|         network.remove(network.lines)
 | |
|         network.scale(0.7)
 | |
|         ledgers = self.get_distributed_ledgers()
 | |
|         self.add(network, ledgers)
 | |
|         VGroup(network, ledgers).to_edge(RIGHT)
 | |
| 
 | |
|     def add_block_creators(self):
 | |
|         block_creators = VGroup()
 | |
|         labels = VGroup()
 | |
|         everything = VGroup()
 | |
|         for x in range(self.n_block_creators):
 | |
|             block_creator = PiCreature(color = GREY)
 | |
|             block_creator.set_height(self.alice.get_height())
 | |
|             label = TextMobject("Block creator %d"%(x+1))
 | |
|             label.scale(0.7)
 | |
|             label.next_to(block_creator, DOWN, SMALL_BUFF)
 | |
|             block_creator.label = label
 | |
|             block_creators.add(block_creator)
 | |
|             labels.add(label)
 | |
|             everything.add(VGroup(block_creator, label))
 | |
|         everything.arrange_submobjects(DOWN, buff = LARGE_BUFF)
 | |
|         everything.to_edge(LEFT)
 | |
| 
 | |
|         self.play(LaggedStart(FadeIn, everything))
 | |
|         self.pi_creatures.add(*block_creators)
 | |
|         self.wait()
 | |
| 
 | |
|         self.block_creators = block_creators
 | |
|         self.block_creator_labels = labels
 | |
| 
 | |
|     def broadcast_transactions(self):
 | |
|         payment_parts = [
 | |
|             ("Alice", "Bob", 20),
 | |
|             ("Bob", "Charlie", 10),
 | |
|             ("Charlie", "You", 50),
 | |
|             ("You", "Alice", 30),
 | |
|         ]
 | |
|         payments = VGroup()
 | |
|         payment_targets = VGroup()
 | |
|         for from_name, to_name, amount in payment_parts:
 | |
|             verb = "pay" if from_name == "You" else "pays"
 | |
|             payment = TextMobject(
 | |
|                 from_name, verb, to_name, "%d LD"%amount
 | |
|             )
 | |
|             payment.set_color_by_tex("LD", YELLOW)
 | |
|             for name in self.get_names():
 | |
|                 payment.set_color_by_tex(
 | |
|                     name.capitalize(),
 | |
|                     self.get_color_from_name(name)
 | |
|                 )
 | |
|             payment.scale(0.7)
 | |
|             payment.generate_target()
 | |
|             payment_targets.add(payment.target)
 | |
| 
 | |
|             pi = getattr(self, from_name.lower())
 | |
|             payment.scale(0.1)
 | |
|             payment.set_fill(opacity = 0)
 | |
|             payment.move_to(pi)
 | |
|             payments.add(payment)
 | |
|         payment_targets.arrange_submobjects(DOWN, aligned_edge = LEFT)
 | |
|         payment_targets.next_to(
 | |
|             self.block_creator_labels, RIGHT,
 | |
|             MED_LARGE_BUFF
 | |
|         )
 | |
|         payment_targets.shift(UP)
 | |
| 
 | |
|         anims = []
 | |
|         alpha_range = np.linspace(0, 0.5, len(payments))
 | |
|         for pi, payment, alpha in zip(self.pi_creatures, payments, alpha_range):
 | |
|             rf1 = squish_rate_func(smooth, alpha, alpha+0.5)
 | |
|             rf2 = squish_rate_func(smooth, alpha, alpha+0.5)
 | |
|             anims.append(Broadcast(
 | |
|                 pi, rate_func = rf1,
 | |
|                 big_radius = 3,
 | |
|             ))
 | |
|             anims.append(MoveToTarget(payment, rate_func = rf2))
 | |
| 
 | |
|         self.play(*anims, run_time = 5)
 | |
|         self.payments = payments
 | |
| 
 | |
|     def collect_transactions(self):
 | |
|         creator = self.block_creators[0]
 | |
|         block = self.get_block()
 | |
|         block.stretch_to_fit_height(4)
 | |
|         block.stretch_to_fit_width(3.5)
 | |
|         block.next_to(creator.label, RIGHT, MED_LARGE_BUFF)
 | |
|         block.to_edge(UP)
 | |
| 
 | |
|         payments = self.payments
 | |
|         payments.generate_target()
 | |
|         payments.target.set_height(1.5)
 | |
|         payments.target.move_to(block)
 | |
| 
 | |
|         prev_hash = TextMobject("Prev hash")
 | |
|         prev_hash.set_color(BLUE)
 | |
|         prev_hash.set_height(0.3)
 | |
|         prev_hash.next_to(block.get_top(), DOWN, MED_SMALL_BUFF)
 | |
|         block.add(prev_hash)
 | |
| 
 | |
|         self.play(
 | |
|             FadeIn(block),
 | |
|             MoveToTarget(payments),
 | |
|             creator.change, "raise_right_hand"
 | |
|         )
 | |
|         self.wait()
 | |
|         block.add(payments)
 | |
| 
 | |
|         self.block = block
 | |
| 
 | |
|     def find_proof_of_work(self):
 | |
|         block = self.block
 | |
| 
 | |
|         arrow = Arrow(UP, ORIGIN, buff = 0)
 | |
|         arrow.next_to(block, DOWN)
 | |
|         sha = TextMobject("SHA256")
 | |
|         sha.scale(0.7)
 | |
|         sha.next_to(arrow, RIGHT)
 | |
|         arrow.add(sha)
 | |
| 
 | |
|         self.add(arrow)
 | |
|         for x in range(self.n_pow_guesses):
 | |
|             guess = Integer(random.randint(10**11, 10**12))
 | |
|             guess.set_color(GREEN)
 | |
|             guess.set_height(0.3)
 | |
|             guess.next_to(block.get_bottom(), UP, MED_SMALL_BUFF)
 | |
| 
 | |
|             if x == self.n_pow_guesses - 1:
 | |
|                 digest = sha256_tex_mob(str(x), 60)
 | |
|                 VGroup(*digest[:60]).set_color(YELLOW)
 | |
|             else:
 | |
|                 digest = sha256_tex_mob(str(x))
 | |
|             digest.set_width(block.get_width())
 | |
|             digest.next_to(arrow.get_end(), DOWN)
 | |
| 
 | |
|             self.add(guess, digest)
 | |
|             self.wait(1./20)
 | |
|             self.remove(guess, digest)
 | |
|         proof_of_work = guess
 | |
|         self.add(proof_of_work, digest)
 | |
|         block.add(proof_of_work)
 | |
|         self.wait()
 | |
| 
 | |
|         self.hash_group = VGroup(arrow, digest)
 | |
| 
 | |
|     def add_block_reward(self):
 | |
|         payments = self.payments
 | |
|         new_transaction = TextMobject(
 | |
|             self.block_creator_labels[0].get_tex_string(),
 | |
|             "gets", "10 LD"
 | |
|         )
 | |
|         new_transaction[0].set_color(LIGHT_GREY)
 | |
|         new_transaction.set_color_by_tex("LD", YELLOW)
 | |
|         new_transaction.set_height(payments[0].get_height())
 | |
|         new_transaction.move_to(payments.get_top())
 | |
|         payments.generate_target()
 | |
|         payments.target.next_to(new_transaction, DOWN, SMALL_BUFF, LEFT)
 | |
|         new_transaction.shift(SMALL_BUFF*UP)
 | |
| 
 | |
|         self.play(
 | |
|             MoveToTarget(payments),
 | |
|             Write(new_transaction)
 | |
|         )
 | |
|         payments.add_to_back(new_transaction)
 | |
|         self.wait()
 | |
| 
 | |
|     def comment_on_block_reward(self):
 | |
|         reward = self.payments[0]
 | |
|         reward_rect = SurroundingRectangle(reward)
 | |
|         big_rect = SurroundingRectangle(self.ledgers)
 | |
|         big_rect.set_stroke(width = 0)
 | |
|         big_rect.set_fill(BLACK, opacity = 1)
 | |
| 
 | |
|         comments = VGroup(*list(map(TextMobject, [
 | |
|             "- ``Block reward''",
 | |
|             "- No sender/signature",
 | |
|             "- Adds to total money supply",
 | |
|         ])))
 | |
|         comments.arrange_submobjects(DOWN, aligned_edge = LEFT)
 | |
|         comments.move_to(big_rect, UP+LEFT)
 | |
| 
 | |
|         pi_creatures = self.pi_creatures
 | |
|         self.pi_creatures = VGroup()
 | |
| 
 | |
|         self.play(ShowCreation(reward_rect))
 | |
|         self.play(FadeIn(big_rect))
 | |
|         for comment in comments:
 | |
|             self.play(FadeIn(comment))
 | |
|             self.wait(2)
 | |
|         self.play(*list(map(FadeOut, [big_rect, comments, reward_rect])))
 | |
| 
 | |
|         self.pi_creatures = pi_creatures
 | |
| 
 | |
|     def write_miners(self):
 | |
|         for label in self.block_creator_labels:
 | |
|             tex = label.get_tex_string()
 | |
|             new_label = TextMobject("Miner " + tex[-1])
 | |
|             new_label.set_color(label.get_color())
 | |
|             new_label.replace(label, dim_to_match = 1)
 | |
|             self.play(Transform(label, new_label))
 | |
|         top_payment = self.payments[0]
 | |
|         new_top_payment = TextMobject("Miner 1", "gets", "10 LD")
 | |
|         new_top_payment[0].set_color(LIGHT_GREY)
 | |
|         new_top_payment[-1].set_color(YELLOW)
 | |
|         new_top_payment.set_height(top_payment.get_height())
 | |
|         new_top_payment.move_to(top_payment, LEFT)
 | |
|         self.play(Transform(top_payment, new_top_payment))
 | |
|         self.wait()
 | |
| 
 | |
|     def broadcast_block(self):
 | |
|         old_chains = self.block_chains
 | |
|         self.n_blocks = 4
 | |
|         new_chains = self.get_distributed_ledgers()
 | |
|         block_target_group = VGroup()
 | |
|         anims = []
 | |
|         arrow_creations = []
 | |
|         for old_chain, new_chain  in zip(old_chains, new_chains):
 | |
|             for attr in "blocks", "arrows":
 | |
|                 pairs = list(zip(
 | |
|                     getattr(old_chain, attr), 
 | |
|                     getattr(new_chain, attr), 
 | |
|                 ))
 | |
|                 for m1, m2 in pairs:
 | |
|                     anims.append(Transform(m1, m2))
 | |
|             arrow_creations.append(ShowCreation(new_chain.arrows[-1]))
 | |
|             block_target = self.block.copy()
 | |
|             block_target.replace(new_chain.blocks[-1], stretch = True)
 | |
|             block_target_group.add(block_target)
 | |
|         anims.append(Transform(
 | |
|             VGroup(self.block),
 | |
|             block_target_group
 | |
|         ))
 | |
|         anims.append(Broadcast(self.block, n_circles = 4))
 | |
|         anims.append(FadeOut(self.hash_group))
 | |
|         anims.append(ApplyMethod(
 | |
|             self.block_creators[0].change, "happy"
 | |
|         ))
 | |
| 
 | |
|         self.play(*anims, run_time = 2)
 | |
|         self.play(*it.chain(
 | |
|             arrow_creations,
 | |
|             [
 | |
|                 ApplyMethod(
 | |
|                     pi.change, "hooray", 
 | |
|                     pi.block_chain.get_right()
 | |
|                 )
 | |
|                 for pi in self.pi_creatures
 | |
|             ]
 | |
|         ))
 | |
|         self.wait(3)
 | |
| 
 | |
| class MiningIsALottery(IntroduceBlockCreator):
 | |
|     CONFIG = {
 | |
|         "n_miners" : 3,
 | |
|         "denomination" : "LD",
 | |
|         "n_guesses" : 90,
 | |
|         "n_nonce_digits" : 15,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_blocks()
 | |
|         self.add_arrows()
 | |
|         self.make_guesses()
 | |
| 
 | |
|     def create_pi_creatures(self):
 | |
|         IntroduceBlockCreator.create_pi_creatures(self)
 | |
|         miners = VGroup(*[
 | |
|             PiCreature(color = GREY)
 | |
|             for n in range(self.n_miners)
 | |
|         ])
 | |
|         miners.scale(0.5)
 | |
|         miners.arrange_submobjects(DOWN, buff = LARGE_BUFF)
 | |
|         miners.to_edge(LEFT)
 | |
|         for x, miner in enumerate(miners):
 | |
|             label = TextMobject("Miner %d"%(x+1))
 | |
|             label.scale(0.7)
 | |
|             label.next_to(miner, DOWN, SMALL_BUFF)
 | |
|             miner.label = label
 | |
|             self.add(label)
 | |
|         self.miners = miners
 | |
|         return miners
 | |
| 
 | |
|     def add_blocks(self):
 | |
|         self.add(self.miners)
 | |
| 
 | |
|         blocks = VGroup()
 | |
|         for miner in self.miners:
 | |
|             block = self.get_block()
 | |
|             block.stretch_to_fit_height(2)
 | |
|             block.stretch_to_fit_width(3)
 | |
|             block.next_to(miner, RIGHT)
 | |
| 
 | |
|             payments = self.get_payments(miner)
 | |
|             payments.set_height(1)
 | |
|             payments.move_to(block)
 | |
|             block.add(payments)
 | |
| 
 | |
|             prev_hash = TextMobject("Prev hash")
 | |
|             prev_hash.set_color(BLUE)
 | |
|             prev_hash.set_height(0.2)
 | |
|             prev_hash.next_to(block.get_top(), DOWN, SMALL_BUFF)
 | |
|             block.add(prev_hash)
 | |
| 
 | |
|             miner.block = block
 | |
|             miner.change("pondering", block)
 | |
|             blocks.add(block)
 | |
| 
 | |
|         self.blocks = blocks
 | |
|         self.add(blocks)
 | |
| 
 | |
|     def add_arrows(self):
 | |
|         self.arrows = VGroup()
 | |
|         for block in self.blocks:
 | |
|             arrow = Arrow(LEFT, RIGHT)
 | |
|             arrow.next_to(block)
 | |
|             label = TextMobject("SHA256")
 | |
|             label.scale(0.7)
 | |
|             label.next_to(arrow, UP, buff = SMALL_BUFF)
 | |
|             self.add(arrow, label)
 | |
|             block.arrow = arrow
 | |
|             self.arrows.add(VGroup(arrow, label))
 | |
| 
 | |
|     def make_guesses(self):
 | |
|         for x in range(self.n_guesses):
 | |
|             e = self.n_nonce_digits
 | |
|             nonces = VGroup()
 | |
|             digests = VGroup()
 | |
|             for block in self.blocks:
 | |
|                 nonce = Integer(random.randint(10**e, 10**(e+1)))
 | |
|                 nonce.set_height(0.2)
 | |
|                 nonce.next_to(block.get_bottom(), UP, SMALL_BUFF)
 | |
|                 nonces.add(nonce)
 | |
|                 digest = sha256_tex_mob(str(x) + str(block))
 | |
|                 digest.set_height(block.get_height())
 | |
|                 digest.next_to(block.arrow, RIGHT)
 | |
|                 digests.add(digest)
 | |
|             self.add(nonces, digests)
 | |
|             self.wait(1./20)
 | |
|             self.remove(nonces, digests)
 | |
|         self.add(nonces, digests)
 | |
| 
 | |
|         winner_index = 1
 | |
|         winner = self.miners[winner_index]
 | |
|         losers = VGroup(*[m for m in self.miners if m is not winner])
 | |
| 
 | |
|         nonces[winner_index].set_color(GREEN)
 | |
|         new_digest = sha256_tex_mob("Winner", 60)
 | |
|         VGroup(*new_digest[:60]).set_color(YELLOW)
 | |
|         old_digest = digests[winner_index]
 | |
|         new_digest.replace(old_digest)
 | |
|         Transform(old_digest, new_digest).update(1)
 | |
| 
 | |
|         self.play(
 | |
|             winner.change, "hooray",
 | |
|             *[
 | |
|                 ApplyMethod(VGroup(
 | |
|                     self.blocks[i], self.arrows[i], 
 | |
|                     nonces[i], digests[i]
 | |
|                 ).fade, 0.7)
 | |
|                 for i in range(len(self.blocks))
 | |
|                 if i is not winner_index
 | |
|             ]
 | |
|         )
 | |
|         self.play(*[
 | |
|             ApplyMethod(loser.change, "angry", winner)
 | |
|             for loser in losers
 | |
|         ])
 | |
|         self.wait(2)
 | |
| 
 | |
|     #####
 | |
| 
 | |
|     def get_payments(self, miner):
 | |
|         if not hasattr(self, "ledger"):
 | |
|             self.get_ledger() ##Unused
 | |
|         self.ledger.content.remove(*self.ledger.content[1:])
 | |
| 
 | |
|         lines = VGroup()
 | |
| 
 | |
|         miner_name = miner.label.get_tex_string()
 | |
|         top_line = TextMobject(miner_name, "gets", "10 LD")
 | |
|         top_line.set_color_by_tex(miner_name, LIGHT_GREY)
 | |
|         top_line.set_color_by_tex("LD", YELLOW)
 | |
|         lines.add(top_line)
 | |
|         payments = [
 | |
|             ("Alice", "Bob", 20),
 | |
|             ("Charlie", "You", 50),
 | |
|         ]
 | |
|         for payment in payments:
 | |
|             lines.add(self.add_payment_line_to_ledger(*payment))
 | |
|         lines.add(TexMobject("\\vdots"))
 | |
|         for line in lines:
 | |
|             line.set_height(0.5)
 | |
|         lines.arrange_submobjects(
 | |
|             DOWN, buff = SMALL_BUFF, aligned_edge = LEFT
 | |
|         )
 | |
|         lines[-1].next_to(lines[-2], DOWN, buff = SMALL_BUFF)
 | |
|         return lines
 | |
| 
 | |
| class TwoBlockChains(DistributedBlockChainScene):
 | |
|     CONFIG = {
 | |
|         "n_blocks" : 5,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.listen_for_new_blocks()
 | |
|         self.defer_to_longer()
 | |
|         self.break_tie()
 | |
| 
 | |
|     def listen_for_new_blocks(self):
 | |
|         randy = self.randy
 | |
|         chain = self.get_block_chain()
 | |
|         chain.scale(1.5)
 | |
|         chain.next_to(randy, UP+RIGHT)
 | |
|         chain.shift_onto_screen()
 | |
|         randy.change("raise_right_hand", chain)
 | |
| 
 | |
|         corners = [
 | |
|             u1 * FRAME_X_RADIUS*RIGHT + u2 * FRAME_Y_RADIUS*UP
 | |
|             for u1, u2 in it.product(*[[-1, 1]]*2)
 | |
|         ]
 | |
|         moving_blocks = chain.blocks[1:]
 | |
| 
 | |
|         self.add(randy, chain.blocks[0])
 | |
|         for corner, block, arrow in zip(corners, moving_blocks, chain.arrows):
 | |
|             block.save_state()
 | |
|             block.next_to(corner, corner)
 | |
|             self.play(
 | |
|                 ApplyMethod(
 | |
|                     block.restore,
 | |
|                     rate_func = squish_rate_func(smooth, 0.3, 0.8),
 | |
|                     run_time = 3,
 | |
|                 ),
 | |
|                 Broadcast(corner, run_time = 3),
 | |
|                 ShowCreation(
 | |
|                     arrow, 
 | |
|                     rate_func = squish_rate_func(smooth, 0.8, 1),
 | |
|                     run_time = 3,
 | |
|                 ),
 | |
|             )
 | |
|         self.wait()
 | |
| 
 | |
|         self.block_chain = chain
 | |
| 
 | |
|     def defer_to_longer(self):
 | |
|         randy = self.randy
 | |
|         self.n_blocks -= 1
 | |
|         block_chains = VGroup(
 | |
|             self.block_chain,
 | |
|             self.get_block_chain().scale(1.5)
 | |
|         )
 | |
|         block_chains[1].next_to(randy, UP+LEFT)
 | |
|         block_chains[1].shift_onto_screen()
 | |
| 
 | |
|         conflicting = TextMobject("Conflicting")
 | |
|         conflicting.to_edge(UP)
 | |
|         conflicting.set_color(RED)
 | |
|         arrows = VGroup(*[
 | |
|             Arrow(
 | |
|                 conflicting.get_bottom(), block_chain.get_top(),
 | |
|                 color = RED,
 | |
|                 buff = MED_LARGE_BUFF
 | |
|             )
 | |
|             for block_chain in block_chains
 | |
|         ])
 | |
| 
 | |
|         longer_chain_rect = SurroundingRectangle(block_chains[0])
 | |
|         longer_chain_rect.set_stroke(GREEN, 8)
 | |
|         checkmark = TexMobject("\\checkmark")
 | |
|         checkmark.set_color(GREEN)
 | |
|         checkmark.next_to(longer_chain_rect, UP)
 | |
|         checkmark.shift(RIGHT)
 | |
| 
 | |
|         chain = block_chains[1]
 | |
|         chain.save_state()
 | |
|         corner = FRAME_X_RADIUS*LEFT + FRAME_Y_RADIUS*UP
 | |
|         chain.next_to(corner, UP+LEFT)
 | |
|         self.play(
 | |
|             randy.change, "confused", chain,
 | |
|             Broadcast(corner),
 | |
|             ApplyMethod(
 | |
|                 chain.restore,
 | |
|                 rate_func = squish_rate_func(smooth, 0.3, 0.7),
 | |
|                 run_time = 3
 | |
|             )
 | |
|         )
 | |
|         self.play(
 | |
|             Write(conflicting),
 | |
|             *list(map(ShowCreation, arrows))
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(ShowCreation(longer_chain_rect))
 | |
|         self.play(Write(checkmark, run_time = 1))
 | |
|         self.play(randy.change, "thinking", checkmark)
 | |
|         self.wait()
 | |
| 
 | |
|         self.to_fade = VGroup(
 | |
|             conflicting, arrows, 
 | |
|             longer_chain_rect, checkmark
 | |
|         )
 | |
|         self.block_chains = block_chains
 | |
| 
 | |
|     def break_tie(self):
 | |
|         to_fade = self.to_fade
 | |
|         block_chains = self.block_chains
 | |
|         randy = self.randy
 | |
| 
 | |
|         arrow = block_chains[1].arrows[-1]
 | |
|         block = block_chains[1].blocks[-1]
 | |
|         arrow_block = VGroup(arrow, block).copy()
 | |
| 
 | |
|         block_chains.generate_target()
 | |
|         block_chains.target.arrange_submobjects(
 | |
|             DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT
 | |
|         )
 | |
|         block_chains.target.next_to(randy, UP)
 | |
|         block_chains.target.to_edge(LEFT)
 | |
| 
 | |
|         self.play(
 | |
|             MoveToTarget(block_chains),
 | |
|             FadeOut(to_fade),
 | |
|             run_time = 1
 | |
|         )
 | |
|         arrow_block.next_to(block_chains[1], RIGHT, buff = 0)
 | |
|         block_chains[1].add(arrow_block)
 | |
|         self.play(
 | |
|             randy.change, "confused", block_chains,
 | |
|             FadeIn(arrow_block),
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
|         arrow_block = arrow_block.copy()
 | |
|         arrow_block.next_to(FRAME_X_RADIUS*RIGHT, RIGHT)
 | |
|         self.play(
 | |
|             ApplyMethod(
 | |
|                 arrow_block.next_to, block_chains[0], RIGHT, 0,
 | |
|                 run_time = 3,
 | |
|                 rate_func = squish_rate_func(smooth, 0.3, 0.8)
 | |
|             ),
 | |
|             Broadcast(arrow_block),
 | |
|         )
 | |
|         block_chains[0].add(arrow_block)
 | |
|         rect = SurroundingRectangle(block_chains[0])
 | |
|         rect.set_stroke(GREEN, 8)
 | |
|         checkmark = TexMobject("\\checkmark")
 | |
|         checkmark.next_to(rect, UP)
 | |
|         checkmark.set_color(GREEN)
 | |
| 
 | |
|         self.play(
 | |
|             ShowCreation(rect),
 | |
|             Write(checkmark),
 | |
|             randy.change, "happy", arrow_block
 | |
|         )
 | |
|         self.wait(2)
 | |
| 
 | |
| 
 | |
|     ####
 | |
| 
 | |
|     def create_pi_creatures(self):
 | |
|         randy = Randolph()
 | |
|         randy.to_edge(DOWN)
 | |
|         self.randy = randy
 | |
|         return VGroup(randy)
 | |
| 
 | |
| class ReplaceCentralAuthorityWithWork(Scene):
 | |
|     def construct(self):
 | |
|         trust, central = words = TextMobject("Trust", "central authority")
 | |
|         words.scale(1.5)
 | |
|         cross = Cross(central)
 | |
|         work = TextMobject("computational work")
 | |
|         work.scale(1.5)
 | |
|         work.move_to(central, LEFT)
 | |
|         work.set_color(YELLOW)
 | |
| 
 | |
|         self.play(Write(words))
 | |
|         self.play(ShowCreation(cross))
 | |
|         central.add(cross)
 | |
|         self.play(
 | |
|             central.shift, DOWN,
 | |
|             Write(work)
 | |
|         )
 | |
|         self.wait()
 | |
| 
 | |
| class AskAboutTrustingWork(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         mode = "raise_left_hand"
 | |
|         self.student_says(
 | |
|             "Is trusting work \\\\ really enough?",
 | |
|             target_mode = mode,
 | |
|         )
 | |
|         self.change_student_modes("confused", mode, "erm")
 | |
|         self.wait(3)
 | |
|         self.teacher_says(
 | |
|             "Well, let's try\\\\ fooling someone",
 | |
|             target_mode = "speaking"
 | |
|         )
 | |
|         self.wait(2)
 | |
| 
 | |
| class DoubleSpendingAttack(DistributedBlockChainScene):
 | |
|     CONFIG = {
 | |
|         "fraud_block_height" : 3,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.initialize_characters()
 | |
|         self.show_fraudulent_block()
 | |
|         self.send_to_bob()
 | |
|         self.dont_send_to_rest_of_network()
 | |
| 
 | |
|     def initialize_characters(self):
 | |
|         network = self.get_large_network()
 | |
|         network.scale(0.7)
 | |
|         block_chains = self.get_distributed_ledgers()
 | |
|         self.add(network, block_chains)
 | |
|         self.play(self.alice.change, "conniving")
 | |
|         self.wait()
 | |
| 
 | |
|     def show_fraudulent_block(self):
 | |
|         block = self.get_fraud_block()
 | |
|         block.next_to(self.alice, LEFT, LARGE_BUFF)
 | |
|         block.remove(block.content)
 | |
|         self.play(
 | |
|             ShowCreation(block),
 | |
|             Write(block.content),
 | |
|             self.alice.change, "raise_left_hand"
 | |
|         )
 | |
|         block.add(block.content)
 | |
|         self.wait()
 | |
| 
 | |
|         self.block = block
 | |
| 
 | |
|     def send_to_bob(self):
 | |
|         block = self.block.copy()
 | |
|         block.generate_target()
 | |
|         block.target.replace(
 | |
|             self.bob.block_chain.blocks[-1],
 | |
|             stretch = True
 | |
|         )
 | |
|         arrow = self.bob.block_chain.arrows[-1].copy()
 | |
|         VGroup(arrow, block.target).next_to(
 | |
|             self.bob.block_chain.blocks[-1], RIGHT, buff = 0
 | |
|         )
 | |
| 
 | |
|         self.play(
 | |
|             MoveToTarget(block),
 | |
|             self.alice.change, "happy"
 | |
|         )
 | |
|         self.play(ShowCreation(arrow))
 | |
|         self.wait()
 | |
| 
 | |
|     def dont_send_to_rest_of_network(self):
 | |
|         bubble = ThoughtBubble()
 | |
|         words = TextMobject("Alice", "never \\\\ paid", "Bob")
 | |
|         for name in "Alice", "Bob":
 | |
|             words.set_color_by_tex(name, self.get_color_from_name(name))
 | |
|         bubble.add_content(words)
 | |
|         bubble.resize_to_content()
 | |
|         bubble.add(*bubble.content)
 | |
|         bubble.move_to(self.you.get_corner(UP+RIGHT), DOWN+LEFT)
 | |
| 
 | |
|         self.play(
 | |
|             self.charlie.change, "shruggie",
 | |
|             self.you.change, "shruggie",
 | |
|         )
 | |
|         self.play(LaggedStart(FadeIn, bubble))
 | |
|         self.play(self.bob.change, "confused", words)
 | |
|         self.wait(2)
 | |
| 
 | |
|     ###
 | |
| 
 | |
|     def get_fraud_block(self):
 | |
|         block = self.get_block()
 | |
|         block.set_height(self.fraud_block_height)
 | |
|         content = VGroup()
 | |
| 
 | |
|         tuples = [
 | |
|             ("Prev hash", UP, BLUE), 
 | |
|             ("Proof of work", DOWN, GREEN),
 | |
|         ]
 | |
|         for word, vect, color in tuples:
 | |
|             mob = TextMobject(word)
 | |
|             mob.set_color(color)
 | |
|             mob.set_height(0.07*block.get_height())
 | |
|             mob.next_to(
 | |
|                 block.get_edge_center(vect), -vect,
 | |
|                 buff = 0.06*block.get_height()
 | |
|             )
 | |
|             content.add(mob)
 | |
|             attr = word.lower().replace(" ", "_")
 | |
|             setattr(block, attr, mob)
 | |
| 
 | |
|         payment = TextMobject("Alice", "pays", "Bob", "100 LD")
 | |
|         for name in "Alice", "Bob":
 | |
|             payment.set_color_by_tex(name, self.get_color_from_name(name))
 | |
|         payment.set_color_by_tex("LD", YELLOW)
 | |
|         payments = VGroup(
 | |
|             TexMobject("\\vdots"),
 | |
|             payment,
 | |
|             TexMobject("\\vdots")
 | |
|         )
 | |
|         payments.arrange_submobjects(DOWN)
 | |
|         payments.set_width(0.9*block.get_width())
 | |
|         payments.move_to(block)
 | |
|         content.add(payments)
 | |
| 
 | |
|         block.content = content
 | |
|         block.add(content)
 | |
|         return block
 | |
| 
 | |
| class AliceRacesOtherMiners(DoubleSpendingAttack):
 | |
|     CONFIG = {
 | |
|         "n_frames_per_pow" : 3,
 | |
|         "fraud_block_height" : 2,
 | |
|         "n_miners" : 3,
 | |
|         "n_proof_of_work_digits" : 11,
 | |
|         "proof_of_work_update_counter" : 0,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.initialize_characters()
 | |
|         self.find_proof_of_work()
 | |
|         self.receive_broadcast_from_other_miners()
 | |
|         self.race_between_alice_and_miners()
 | |
| 
 | |
|     def initialize_characters(self):
 | |
|         alice = self.alice
 | |
|         bob = self.bob
 | |
|         alice_l = VGroup(alice, alice.label)
 | |
|         bob_l = VGroup(bob, bob.label)
 | |
|         bob_l.move_to(ORIGIN)
 | |
|         alice_l.to_corner(UP+LEFT)
 | |
|         alice_l.shift(DOWN)
 | |
|         self.add(bob_l, alice_l)
 | |
| 
 | |
|         chain = self.get_block_chain()
 | |
|         chain.next_to(bob, UP)
 | |
|         self.block_chain = chain
 | |
|         self.add(chain)
 | |
| 
 | |
|         fraud_block = self.get_fraud_block()
 | |
|         fraud_block.next_to(alice, RIGHT)
 | |
|         fraud_block.to_edge(UP)
 | |
|         proof_of_work = self.get_rand_int_mob()
 | |
|         proof_of_work.replace(fraud_block.proof_of_work, dim_to_match = 1)
 | |
|         fraud_block.content.remove(fraud_block.proof_of_work)
 | |
|         fraud_block.content.add(proof_of_work)
 | |
|         fraud_block.proof_of_work = proof_of_work
 | |
|         self.fraud_block = fraud_block
 | |
|         self.add(fraud_block)
 | |
| 
 | |
|         miners = VGroup(*[
 | |
|             PiCreature(color = GREY)
 | |
|             for x in range(self.n_miners)
 | |
|         ])
 | |
|         miners.set_height(alice.get_height())
 | |
|         miners.arrange_submobjects(RIGHT, buff = LARGE_BUFF)
 | |
|         miners.to_edge(DOWN+LEFT)
 | |
|         miners.shift(0.5*UP)
 | |
|         miners_word = TextMobject("Miners")
 | |
|         miners_word.next_to(miners, DOWN)
 | |
|         self.miners = miners
 | |
|         self.add(miners_word, miners)
 | |
| 
 | |
|         miner_blocks = VGroup()
 | |
|         self.proofs_of_work = VGroup()
 | |
|         self.add_foreground_mobject(self.proofs_of_work)
 | |
|         for miner in miners:
 | |
|             block = self.get_block()
 | |
|             block.set_width(1.5*miner.get_width())
 | |
|             block.next_to(miner, UP)
 | |
| 
 | |
|             transactions = self.get_block_filler(block)
 | |
|             block.add(transactions)
 | |
| 
 | |
|             proof_of_work = self.get_rand_int_mob()
 | |
|             prev_hash = TextMobject("Prev hash").set_color(BLUE)
 | |
|             for mob, vect in (proof_of_work, DOWN), (prev_hash, UP):
 | |
|                 mob.set_height(0.1*block.get_height())
 | |
|                 mob.next_to(
 | |
|                     block.get_edge_center(vect), -vect, 
 | |
|                     buff = 0.05*block.get_height()
 | |
|                 )
 | |
|                 block.add(mob)
 | |
|             block.proof_of_work = proof_of_work
 | |
|             block.prev_hash = prev_hash
 | |
|             self.proofs_of_work.add(proof_of_work)
 | |
| 
 | |
|             miner.block = block
 | |
|             miner_blocks.add(block)
 | |
|         self.add(miner_blocks)
 | |
| 
 | |
|     def find_proof_of_work(self):
 | |
|         fraud_block = self.fraud_block
 | |
|         chain = self.block_chain
 | |
| 
 | |
|         self.proofs_of_work.add(self.fraud_block.proof_of_work)
 | |
|         self.wait(3)
 | |
|         self.proofs_of_work.remove(self.fraud_block.proof_of_work)
 | |
|         fraud_block.proof_of_work.set_color(GREEN)
 | |
|         self.play(
 | |
|             Indicate(fraud_block.proof_of_work),
 | |
|             self.alice.change, "hooray"
 | |
|         )
 | |
|         self.wait()
 | |
|     
 | |
|         block = fraud_block.copy()        
 | |
|         block.generate_target()
 | |
|         block.target.replace(chain.blocks[-1], stretch = True)
 | |
|         arrow = chain.arrows[-1].copy()
 | |
|         VGroup(arrow, block.target).next_to(
 | |
|             chain.blocks[-1], RIGHT, buff = 0
 | |
|         )
 | |
|         self.remove(fraud_block)
 | |
|         self.clean_fraud_block_content()
 | |
|         self.fraud_fork_head = block
 | |
|         self.fraud_fork_arrow = arrow
 | |
| 
 | |
|         self.play(MoveToTarget(block))
 | |
|         self.play(ShowCreation(arrow))
 | |
|         self.play(self.alice.change, "happy")
 | |
|         self.wait()
 | |
| 
 | |
|     def receive_broadcast_from_other_miners(self):
 | |
|         winner = self.miners[-1]
 | |
|         proof_of_work = winner.block.proof_of_work
 | |
|         self.proofs_of_work.remove(proof_of_work)
 | |
|         proof_of_work.set_color(GREEN)
 | |
|         self.play(
 | |
|             Indicate(proof_of_work),
 | |
|             winner.change, "hooray"
 | |
|         )
 | |
|         block = winner.block.copy()
 | |
|         proof_of_work.set_color(WHITE)
 | |
|         self.remove(winner.block)
 | |
| 
 | |
|         ff_head = self.fraud_fork_head
 | |
|         ff_arrow = self.fraud_fork_arrow
 | |
|         arrow = ff_arrow.copy()
 | |
|         movers = [ff_head, ff_arrow, block, arrow]
 | |
|         for mover in movers:
 | |
|             mover.generate_target()
 | |
|         block.target.replace(ff_head, stretch = True)
 | |
| 
 | |
|         dist = 0.5*ff_head.get_height() + MED_SMALL_BUFF
 | |
|         block.target.shift(dist*DOWN)
 | |
|         ff_head.target.shift(dist*UP)
 | |
|         arrow.target[1].shift(dist*DOWN)
 | |
|         arrow.target.points[-2:] += dist*DOWN
 | |
|         ff_arrow.target[1].shift(dist*UP)
 | |
|         ff_arrow.target.points[-2:] += dist*UP
 | |
| 
 | |
|         self.play(
 | |
|             Broadcast(block),
 | |
|             *[
 | |
|                 MoveToTarget(
 | |
|                     mover, run_time = 3, 
 | |
|                     rate_func = squish_rate_func(smooth, 0.3, 0.8)
 | |
|                 )
 | |
|                 for mover in movers
 | |
|             ]
 | |
|         )
 | |
|         self.play(
 | |
|             FadeIn(winner.block),
 | |
|             winner.change_mode, "plain",
 | |
|             self.alice.change, "sassy",
 | |
|         )
 | |
|         self.proofs_of_work.add(winner.block.proof_of_work)
 | |
|         self.wait(2)
 | |
|         self.play(
 | |
|             self.alice.change, "pondering",
 | |
|             FadeIn(self.fraud_block)
 | |
|         )
 | |
|         self.proofs_of_work.add(self.fraud_block.proof_of_work)
 | |
| 
 | |
|         self.valid_fork_head = block
 | |
| 
 | |
|     def race_between_alice_and_miners(self):
 | |
|         last_fraud_block = self.fraud_fork_head
 | |
|         last_valid_block = self.valid_fork_head
 | |
|         chain = self.block_chain
 | |
|         winners = [
 | |
|             "Alice", "Alice",
 | |
|             "Miners", "Miners", "Miners",
 | |
|             "Alice", "Miners", "Miners", "Miners",
 | |
|             "Alice", "Miners", "Miners"
 | |
|         ]
 | |
| 
 | |
|         self.revert_to_original_skipping_status()
 | |
|         for winner in winners:
 | |
|             self.wait()
 | |
|             if winner == "Alice":
 | |
|                 block = self.fraud_block
 | |
|                 prev_block = last_fraud_block
 | |
|             else:
 | |
|                 block = random.choice(self.miners).block
 | |
|                 prev_block = last_valid_block
 | |
|             block_copy = block.deepcopy()
 | |
|             block_copy.proof_of_work.set_color(GREEN)
 | |
|             block_copy.generate_target()
 | |
|             block_copy.target.replace(chain.blocks[1], stretch = True)
 | |
|             arrow = chain.arrows[0].copy()
 | |
|             VGroup(arrow, block_copy.target).next_to(
 | |
|                 prev_block, RIGHT, buff = 0
 | |
|             )
 | |
|             anims = [
 | |
|                 MoveToTarget(block_copy, run_time = 2),
 | |
|                 ShowCreation(
 | |
|                     arrow,
 | |
|                     run_time = 2,
 | |
|                     rate_func = squish_rate_func(smooth, 0.5, 1),
 | |
|                 ),
 | |
|                 FadeIn(block),
 | |
|             ]
 | |
|             if winner == "Alice":
 | |
|                 last_fraud_block = block_copy
 | |
|             else:
 | |
|                 last_valid_block = block_copy
 | |
|                 anims.append(Broadcast(block, run_time = 2))
 | |
|             self.proofs_of_work.remove(block.proof_of_work)
 | |
|             self.play(*anims)
 | |
|             self.proofs_of_work.add(block.proof_of_work)
 | |
| 
 | |
| 
 | |
|     #####
 | |
| 
 | |
|     def wait(self, time = 1):
 | |
|         self.play(
 | |
|             Animation(VGroup(*self.foreground_mobjects)),
 | |
|             run_time = time
 | |
|         )
 | |
| 
 | |
|     def update_frame(self, *args, **kwargs):
 | |
|         self.update_proofs_of_work()
 | |
|         Scene.update_frame(self, *args, **kwargs)
 | |
| 
 | |
|     def update_proofs_of_work(self):
 | |
|         self.proof_of_work_update_counter += 1
 | |
|         if self.proof_of_work_update_counter%self.n_frames_per_pow != 0:
 | |
|             return
 | |
|         for proof_of_work in self.proofs_of_work:
 | |
|             new_pow = self.get_rand_int_mob()
 | |
|             new_pow.replace(proof_of_work, dim_to_match = 1)
 | |
|             Transform(proof_of_work, new_pow).update(1)
 | |
| 
 | |
|     def get_rand_int_mob(self):
 | |
|         e = self.n_proof_of_work_digits
 | |
|         return Integer(random.randint(10**e, 10**(e+1)))
 | |
| 
 | |
|     def clean_fraud_block_content(self):
 | |
|         content = self.fraud_block.content
 | |
|         payments = content[1]
 | |
|         content.remove(payments)
 | |
|         transactions = self.get_block_filler(self.fraud_block)
 | |
|         content.add(transactions)
 | |
| 
 | |
|     def get_block_filler(self, block):
 | |
|         result = TextMobject("$\\langle$Transactions$\\rangle$")
 | |
|         result.set_width(0.8*block.get_width())
 | |
|         result.move_to(block)
 | |
|         return result
 | |
| 
 | |
| class WhenToTrustANewBlock(DistributedBlockChainScene):
 | |
|     def construct(self):
 | |
|         chain = self.block_chain = self.get_block_chain()
 | |
|         chain.scale(2)
 | |
|         chain.to_edge(LEFT)
 | |
|         self.add(chain)
 | |
| 
 | |
|         words = list(map(TextMobject, [
 | |
|             "Don't trust yet",
 | |
|             "Still don't trust",
 | |
|             "...a little more...",
 | |
|             "Maybe trust",
 | |
|             "Probably safe",
 | |
|             "Alright, you're good."
 | |
|         ]))
 | |
|         colors = [RED, RED, YELLOW, YELLOW, GREEN, GREEN]
 | |
|         self.add_new_block()
 | |
|         arrow = Arrow(UP, DOWN, color = RED)
 | |
|         arrow.next_to(chain.blocks[-1], UP)
 | |
|         for word, color in zip(words, colors):
 | |
|             word.set_color(color)
 | |
|             word.next_to(arrow, UP)
 | |
|         word = words[0]
 | |
|         self.play(
 | |
|             FadeIn(word),
 | |
|             ShowCreation(arrow)
 | |
|         )
 | |
|         for new_word in words[1:]:
 | |
|             kwargs = {
 | |
|                 "run_time" : 3,
 | |
|                 "rate_func" : squish_rate_func(smooth, 0.7, 1)
 | |
|             }
 | |
|             self.add_new_block(
 | |
|                 Transform(word, new_word, **kwargs),
 | |
|                 ApplyMethod(
 | |
|                     arrow.set_color, new_word.get_color(),
 | |
|                     **kwargs
 | |
|                 )
 | |
|             )
 | |
|         self.wait(2)
 | |
| 
 | |
|     def get_block(self):
 | |
|         block = DistributedBlockChainScene.get_block(self)
 | |
|         tuples = [
 | |
|             ("Prev hash", UP, BLUE), 
 | |
|             ("Proof of work", DOWN, GREEN),
 | |
|         ]
 | |
|         for word, vect, color in tuples:
 | |
|             mob = TextMobject(word)
 | |
|             mob.set_color(color)
 | |
|             mob.set_height(0.07*block.get_height())
 | |
|             mob.next_to(
 | |
|                 block.get_edge_center(vect), -vect,
 | |
|                 buff = 0.06*block.get_height()
 | |
|             )
 | |
|             block.add(mob)
 | |
|             attr = word.lower().replace(" ", "_")
 | |
|             setattr(block, attr, mob)
 | |
|         transactions = TextMobject("$\\langle$Transactions$\\rangle$")
 | |
|         transactions.set_width(0.8*block.get_width())
 | |
|         transactions.move_to(block)
 | |
|         block.add(transactions)
 | |
|         return block
 | |
| 
 | |
|     def add_new_block(self, *added_anims):
 | |
|         blocks = self.block_chain.blocks
 | |
|         arrows = self.block_chain.arrows
 | |
|         block = blocks[-1].copy()
 | |
|         arrow = arrows[-1].copy()
 | |
|         VGroup(block, arrow).next_to(blocks[-1], RIGHT, buff = 0)
 | |
|         corner = FRAME_X_RADIUS*RIGHT + FRAME_Y_RADIUS*UP
 | |
|         block.save_state()
 | |
|         block.next_to(corner, UP+RIGHT)
 | |
| 
 | |
|         self.play(
 | |
|             Broadcast(block),
 | |
|             ApplyMethod(
 | |
|                 block.restore,
 | |
|                 run_time = 3,
 | |
|                 rate_func = squish_rate_func(smooth, 0.3, 0.8),
 | |
|             ),
 | |
|             ShowCreation(
 | |
|                 arrow,
 | |
|                 run_time = 3,
 | |
|                 rate_func = squish_rate_func(smooth, 0.7, 1),
 | |
|             ),
 | |
|             *added_anims
 | |
|         )
 | |
|         arrows.add(arrow)
 | |
|         blocks.add(block)
 | |
| 
 | |
| class MainIdeas(Scene):
 | |
|     def construct(self):
 | |
|         title = TextMobject("Main ideas")
 | |
|         title.scale(1.5)
 | |
|         h_line = Line(LEFT, RIGHT)
 | |
|         h_line.set_width(FRAME_X_RADIUS)
 | |
|         h_line.next_to(title, DOWN)
 | |
|         VGroup(title, h_line).to_corner(UP+LEFT)
 | |
| 
 | |
|         ideas = VGroup(*[
 | |
|             TextMobject("$\\cdot$ " + words)
 | |
|             for words in [
 | |
|                 "Digital signatures",
 | |
|                 "The ledger is the currency",
 | |
|                 "Decentralize",
 | |
|                 "Proof of work",
 | |
|                 "Block chain",
 | |
|             ]
 | |
|         ])
 | |
|         colors = BLUE, WHITE, RED, GREEN, YELLOW
 | |
|         for idea, color in zip(ideas, colors):
 | |
|             idea.set_color(color)
 | |
|         ideas.arrange_submobjects(
 | |
|             DOWN,
 | |
|             buff = MED_LARGE_BUFF,
 | |
|             aligned_edge = LEFT
 | |
|         )
 | |
|         ideas.next_to(h_line, DOWN)
 | |
| 
 | |
|         self.add(title, h_line)
 | |
|         for idea in ideas:
 | |
|             self.play(LaggedStart(FadeIn, idea))
 | |
|         self.wait()
 | |
| 
 | |
| class VariableProofOfWork(WhenToTrustANewBlock):
 | |
|     CONFIG = {
 | |
|         "block_height" : 3,
 | |
|         "block_width" : 3,
 | |
|         "n_guesses" : 60,
 | |
|         "n_proof_of_work_digits" : 11,
 | |
|         "n_miners" : 6,
 | |
|     }
 | |
|     def construct(self):
 | |
|         self.add_miner_and_hash()
 | |
|         self.change_requirement()
 | |
|         self.add_more_miners()
 | |
| 
 | |
|     def add_miner_and_hash(self):
 | |
|         miner = PiCreature(color = GREY)
 | |
|         miner.scale(0.7)
 | |
|         miner.to_edge(LEFT)
 | |
| 
 | |
|         block = self.get_block()
 | |
|         block.next_to(miner, RIGHT)
 | |
|         old_proof_of_work = block.proof_of_work
 | |
|         proof_of_work = self.get_rand_int_mob()
 | |
|         proof_of_work.replace(old_proof_of_work, dim_to_match = 1)
 | |
|         block.remove(old_proof_of_work)
 | |
|         block.add(proof_of_work)
 | |
|         block.proof_of_work = proof_of_work
 | |
| 
 | |
|         arrow = Arrow(LEFT, RIGHT)
 | |
|         arrow.next_to(block)
 | |
|         sha_tex = TextMobject("SHA256")
 | |
|         sha_tex.scale(0.7)
 | |
|         sha_tex.next_to(arrow, UP, SMALL_BUFF)
 | |
|         sha_tex.set_color(YELLOW)
 | |
|         arrow.add(sha_tex)
 | |
| 
 | |
|         digest = sha256_tex_mob("Random")
 | |
|         digest.next_to(arrow.get_end(), RIGHT)
 | |
| 
 | |
|         miner.change("pondering", digest)
 | |
| 
 | |
|         self.add(miner, block, arrow, digest)
 | |
|         for x in range(self.n_guesses):
 | |
|             new_pow = self.get_rand_int_mob()
 | |
|             new_pow.replace(proof_of_work, dim_to_match = 1)
 | |
|             Transform(proof_of_work, new_pow).update(1)
 | |
|             if x == self.n_guesses-1:
 | |
|                 n_zeros = 60
 | |
|             else:
 | |
|                 n_zeros = 0
 | |
|             new_digest = sha256_tex_mob(str(x+1), n_zeros)
 | |
|             new_digest.replace(digest)
 | |
|             Transform(digest, new_digest).update(1)
 | |
|             self.wait(1./20)
 | |
|         proof_of_work.set_color(GREEN)
 | |
|         VGroup(*digest[:60]).set_color(YELLOW)
 | |
| 
 | |
|         self.miner = miner
 | |
|         self.block = block
 | |
|         self.arrow = arrow
 | |
|         self.digest = digest
 | |
| 
 | |
|     def change_requirement(self):
 | |
|         digest = self.digest
 | |
|         requirement = TextMobject(
 | |
|             "Must start with \\\\",
 | |
|             "60", "zeros"
 | |
|         )
 | |
|         requirement.next_to(digest, UP, MED_LARGE_BUFF)
 | |
|         self.n_zeros_mob = requirement.get_part_by_tex("60")
 | |
|         self.n_zeros_mob.set_color(YELLOW)
 | |
| 
 | |
|         self.play(Write(requirement, run_time = 2))
 | |
|         self.wait(2)
 | |
|         for n_zeros in 30, 32, 35, 37, 42:
 | |
|             self.change_challenge(n_zeros)
 | |
|             self.wait()
 | |
| 
 | |
|     def add_more_miners(self):
 | |
|         miner = self.miner
 | |
|         block = self.block
 | |
|         miner_block = VGroup(miner, block)
 | |
|         target = miner_block.copy()
 | |
|         target[1].scale(
 | |
|             0.5, about_point = miner.get_right()
 | |
|         )
 | |
|         copies = VGroup(*[
 | |
|             target.copy() 
 | |
|             for x in range(self.n_miners - 1)
 | |
|         ])
 | |
|         everyone = VGroup(target, *copies)
 | |
|         everyone.arrange_submobjects(DOWN)
 | |
|         everyone.set_height(FRAME_HEIGHT - LARGE_BUFF)
 | |
|         everyone.to_corner(UP+LEFT)
 | |
| 
 | |
|         self.play(Transform(miner_block, target))
 | |
|         self.play(LaggedStart(FadeIn, copies))
 | |
|         self.change_challenge(72)
 | |
|         self.wait(2)
 | |
| 
 | |
|     ###
 | |
|     def change_challenge(self, n_zeros):
 | |
|         digest = self.digest
 | |
|         proof_of_work = self.block.proof_of_work
 | |
|         n_zeros_mob = self.n_zeros_mob
 | |
| 
 | |
|         new_digest = sha256_tex_mob(str(n_zeros), n_zeros)
 | |
|         new_digest.move_to(digest)
 | |
|         VGroup(*new_digest[:n_zeros]).set_color(YELLOW)
 | |
|         new_n_zeros_mob = TexMobject(str(n_zeros))
 | |
|         new_n_zeros_mob.move_to(n_zeros_mob)
 | |
|         new_n_zeros_mob.set_color(n_zeros_mob.get_color())
 | |
|         new_pow = self.get_rand_int_mob()
 | |
|         new_pow.replace(proof_of_work, dim_to_match = 1)
 | |
|         new_pow.set_color(proof_of_work.get_color())
 | |
| 
 | |
|         self.play(
 | |
|             Transform(n_zeros_mob, new_n_zeros_mob),
 | |
|             Transform(
 | |
|                 digest, new_digest,
 | |
|                 submobject_mode = "lagged_start"
 | |
|             ),
 | |
|             Transform(proof_of_work, new_pow),
 | |
|         )
 | |
| 
 | |
|     def get_rand_int_mob(self):
 | |
|         e = self.n_proof_of_work_digits
 | |
|         return Integer(random.randint(10**e, 10**(e+1)))
 | |
| 
 | |
| class CompareBlockTimes(Scene):
 | |
|     def construct(self):
 | |
|         title = TextMobject("Average block time")
 | |
|         title.scale(1.5)
 | |
|         title.to_edge(UP)
 | |
|         h_line = Line(LEFT, RIGHT)
 | |
|         h_line.set_width(FRAME_X_RADIUS)
 | |
|         h_line.next_to(title, DOWN, SMALL_BUFF)
 | |
| 
 | |
|         examples = VGroup(
 | |
|             TextMobject("BTC: ", "10 minutes"),
 | |
|             TextMobject("ETH: ", "15 Seconds"),
 | |
|             TextMobject("XRP: ", "3.5 Seconds"),
 | |
|             TextMobject("LTC: ", "2.5 Minutes"),
 | |
|         )
 | |
|         examples.arrange_submobjects(
 | |
|             DOWN, 
 | |
|             buff = LARGE_BUFF,
 | |
|             aligned_edge = LEFT,
 | |
|         )
 | |
|         examples.next_to(h_line, DOWN)
 | |
|         logos = VGroup(
 | |
|             BitcoinLogo(),
 | |
|             EthereumLogo(),
 | |
|             ImageMobject("ripple_logo"),
 | |
|             LitecoinLogo(),
 | |
|         )
 | |
|         colors = [BITCOIN_COLOR, GREEN, BLUE_B, LIGHT_GREY]
 | |
|         for logo, example, color in zip(logos, examples, colors):
 | |
|             logo.set_height(0.5)
 | |
|             logo.next_to(example, LEFT)
 | |
|             example[0].set_color(color)
 | |
| 
 | |
|         self.add(title, h_line)
 | |
|         self.play(
 | |
|             FadeIn(examples[0]),
 | |
|             DrawBorderThenFill(logos[0])
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(*[
 | |
|             LaggedStart(FadeIn, VGroup(*group[1:]))
 | |
|             for group in (examples, logos)
 | |
|         ])
 | |
|         self.wait(2)
 | |
| 
 | |
|     # def get_ethereum_logo(self):
 | |
|     #     logo = SVGMobject(
 | |
|     #         file_name = "ethereum_logo",
 | |
|     #         height = 1,
 | |
|     #     )
 | |
|     #     logo.set_fill(GREEN, 1)
 | |
|     #     logo.set_stroke(WHITE, 3)
 | |
|     #     logo.set_color_by_gradient(GREEN_B, GREEN_D)
 | |
|     #     logo.set_width(1)
 | |
|     #     logo.center()
 | |
|     #     self.add(SurroundingRectangle(logo))
 | |
|     #     return logo
 | |
| 
 | |
| class BlockRewards(Scene):
 | |
|     def construct(self):
 | |
|         title = TextMobject("Block rewards")
 | |
|         title.scale(1.5)
 | |
|         logo = BitcoinLogo()
 | |
|         logo.set_height(0.75)
 | |
|         logo.next_to(title, LEFT)
 | |
|         title.add(logo)
 | |
|         title.to_edge(UP)
 | |
|         h_line = Line(LEFT, RIGHT)
 | |
|         h_line.set_width(FRAME_X_RADIUS)
 | |
|         h_line.next_to(title, DOWN)
 | |
|         self.add(title, logo, h_line)
 | |
| 
 | |
|         rewards = VGroup(
 | |
|             TextMobject("Jan 2009 - Nov 2012:", "50", "BTC"),
 | |
|             TextMobject("Nov 2012 - Jul 2016:", "25", "BTC"),
 | |
|             TextMobject("Jul 2016 - Feb 2020$^*$:", "12.5", "BTC"),
 | |
|             TextMobject("Feb 2020$^*$ - Sep 2023$^*$:", "6.25", "BTC"),
 | |
|         )
 | |
|         rewards.arrange_submobjects(
 | |
|             DOWN, 
 | |
|             buff = MED_LARGE_BUFF,
 | |
|             aligned_edge = LEFT
 | |
|         )
 | |
|         rewards.next_to(h_line, DOWN)
 | |
|         for reward in rewards:
 | |
|             reward[1].set_color(YELLOW)
 | |
| 
 | |
|         footnote = TextMobject(
 | |
|             "$^*$ Extrapolating from the 25 BTC reward period"
 | |
|         )
 | |
|         footnote.scale(0.5)
 | |
|         footnote.to_corner(DOWN+RIGHT)
 | |
| 
 | |
|         self.play(LaggedStart(
 | |
|             FadeIn, rewards,
 | |
|             run_time = 4,
 | |
|             lag_ratio = 0.5
 | |
|         ))
 | |
|         self.play(FadeIn(footnote))
 | |
|         self.wait(3)
 | |
| 
 | |
| class ShowFirstFewBlocks(ExternallyAnimatedScene):
 | |
|     pass
 | |
| 
 | |
| class ShowGeometricSum(Scene):
 | |
|     def construct(self):
 | |
|         equation = TexMobject(
 | |
|             "210{,}000", "(", 
 | |
|             "50", "+", "25", "+", "12.5", "+", 
 | |
|             "6.25", "+\\cdots", ")", "=", "21{,}000{,}000"
 | |
|         )
 | |
|         numbers = ["50", "25", "12.5", "6.25"]
 | |
|         colors = color_gradient([BLUE_D, BLUE_B], 4)
 | |
|         for tex, color in zip(numbers, colors):
 | |
|             equation.set_color_by_tex(tex, color)
 | |
|         equation[-1].set_color(YELLOW)
 | |
| 
 | |
|         self.add(*equation[:2] + equation[-3:-1])
 | |
|         for i in range(2, 9, 2):
 | |
|             self.play(FadeIn(VGroup(*equation[i:i+2])))
 | |
|         self.wait()
 | |
|         self.play(Write(equation[-1]))
 | |
|         self.wait(2)
 | |
| 
 | |
| class TransactionFeeExample(PiCreatureScene):
 | |
|     def construct(self):
 | |
|         alice = self.pi_creature
 | |
|         payment = TextMobject(
 | |
|             "Alice", "pays", "Bob", "0.42 BTC",
 | |
|         )
 | |
|         payment.set_color_by_tex("Alice", BLUE_C)
 | |
|         payment.set_color_by_tex("Bob", MAROON)
 | |
|         payment.set_color_by_tex("BTC", YELLOW)
 | |
|         payment.move_to(2.5*UP)
 | |
| 
 | |
|         fee = TextMobject("And leaves", "0.001 BTC", "to the miner")
 | |
|         fee.set_color_by_tex("BTC", YELLOW)
 | |
|         fee.next_to(payment, DOWN)
 | |
| 
 | |
|         signature = TextMobject(
 | |
|             "$\\langle$Alice's digital signature$\\rangle$"
 | |
|         )
 | |
|         signature.set_color(BLUE_C)
 | |
|         signature.next_to(fee, DOWN)
 | |
| 
 | |
|         group = VGroup(payment, fee, signature)
 | |
|         rect = SurroundingRectangle(group, color = BLUE_B)
 | |
| 
 | |
|         incentive_words = TextMobject(
 | |
|             "Incentivizes miner \\\\ to include"
 | |
|         )
 | |
|         incentive_words.next_to(rect, DOWN, buff = 1.5)
 | |
|         incentive_words.shift(2*RIGHT)
 | |
|         arrow = Arrow(
 | |
|             incentive_words.get_top(),
 | |
|             rect.get_bottom(),
 | |
|             buff = MED_LARGE_BUFF
 | |
|         )
 | |
| 
 | |
|         fee.save_state()
 | |
|         fee.shift(DOWN)
 | |
|         fee.set_fill(opacity = 0)
 | |
| 
 | |
|         self.play(Write(payment))
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             alice.change, "raise_right_hand", payment,
 | |
|             fee.restore,
 | |
|         )
 | |
|         self.play(Write(signature))
 | |
|         self.play(
 | |
|             ShowCreation(rect),
 | |
|             alice.change_mode, "happy"
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             Write(incentive_words),
 | |
|             ShowCreation(arrow),
 | |
|             alice.change, "pondering"
 | |
|         )
 | |
|         self.wait(2)
 | |
| 
 | |
|     def create_pi_creature(self):
 | |
|         alice = PiCreature(color = BLUE_C)
 | |
|         alice.to_edge(DOWN)
 | |
|         alice.shift(FRAME_X_RADIUS*LEFT/2)
 | |
|         return alice
 | |
| 
 | |
| class ShowBitcoinBlockSize(LedgerScene):
 | |
|     CONFIG = {
 | |
|         "denomination" : "BTC"
 | |
|     }
 | |
|     def construct(self):
 | |
|         block = VGroup()
 | |
|         ledger = self.get_ledger()
 | |
| 
 | |
|         payments = VGroup(*[
 | |
|             self.add_payment_line_to_ledger(*args)
 | |
|             for args in [
 | |
|                 ("Alice", "Bob", "0.42"),
 | |
|                 ("You", "Charlie", "3.14"),
 | |
|                 ("Bob", "You", "2.72"),
 | |
|                 ("Alice", "Charlie", "4.67"),
 | |
|             ]
 | |
|         ])
 | |
|         dots = TexMobject("\\vdots")
 | |
|         dots.next_to(payments, DOWN)
 | |
|         payments.add(dots)
 | |
|         payments.to_edge(LEFT)
 | |
|         payments.shift(DOWN+0.5*RIGHT)
 | |
|         payments_rect = SurroundingRectangle(
 | |
|             payments, color = WHITE, buff = MED_LARGE_BUFF
 | |
|         )
 | |
|         block.add(payments_rect, payments)
 | |
| 
 | |
|         tuples = [
 | |
|             ("Prev hash", UP, BLUE_C), 
 | |
|             ("Proof of work", DOWN, GREEN),
 | |
|         ]
 | |
|         for word, vect, color in tuples:
 | |
|             mob = TextMobject(word)
 | |
|             mob.set_color(color)
 | |
|             rect = SurroundingRectangle(
 | |
|                 mob, color = WHITE, buff = MED_SMALL_BUFF
 | |
|             )
 | |
|             VGroup(mob, rect).next_to(payments_rect, vect, 0)
 | |
|             rect.stretch_to_fit_width(payments_rect.get_width())
 | |
|             block.add(mob, rect)
 | |
| 
 | |
|         title = VGroup(
 | |
|             BitcoinLogo(height = 0.75), 
 | |
|             TextMobject("Block").scale(1.5)
 | |
|         )
 | |
|         title.arrange_submobjects(RIGHT, SMALL_BUFF)
 | |
|         title.next_to(block, UP)
 | |
| 
 | |
|         brace = Brace(payments_rect, RIGHT)
 | |
|         limit = brace.get_text(
 | |
|             "Limited to\\\\", 
 | |
|             "$\\sim 2{,}400$", "transactions"
 | |
|         )
 | |
|         limit.set_color_by_tex("2{,}400", RED)
 | |
| 
 | |
|         self.add(title, block)
 | |
|         self.remove(payments)
 | |
|         self.play(
 | |
|             GrowFromCenter(brace),
 | |
|             Write(limit)
 | |
|         )
 | |
|         self.play(LaggedStart(FadeIn, payments))
 | |
|         self.wait()
 | |
| 
 | |
|         ####Visa
 | |
| 
 | |
|         visa_logo = SVGMobject(
 | |
|             file_name = "visa_logo",
 | |
|             height = 0.5,
 | |
|             stroke_width = 0,
 | |
|             fill_color = BLUE_D,
 | |
|             fill_opacity = 1,
 | |
|         )
 | |
|         visa_logo[-1].set_color("#faa61a")
 | |
|         visa_logo.sort_submobjects()
 | |
|         avg_rate = TextMobject("Avg: $1{,}700$/second")
 | |
|         max_rate = TextMobject("Max: $>24{,}000$/second")
 | |
|         rates = VGroup(avg_rate, max_rate)
 | |
|         rates.scale(0.8)
 | |
|         rates.arrange_submobjects(DOWN, aligned_edge = LEFT)
 | |
|         rates.next_to(visa_logo, RIGHT, buff = MED_SMALL_BUFF)
 | |
|         visa = VGroup(visa_logo, rates)
 | |
|         visa.to_corner(UP+RIGHT)
 | |
| 
 | |
|         self.play(LaggedStart(DrawBorderThenFill, visa_logo))
 | |
|         self.play(LaggedStart(FadeIn, avg_rate))
 | |
|         self.wait()
 | |
|         self.play(LaggedStart(FadeIn, max_rate))
 | |
|         self.wait(2)
 | |
| 
 | |
| class CurrentAverageFees(Scene):
 | |
|     def construct(self):
 | |
|         fees = TextMobject(
 | |
|             "Current average fees: ",
 | |
|             "$\\sim 0.0013$ BTC", 
 | |
|             "$\\approx$", "\\$3.39"
 | |
|         )
 | |
|         fees.set_color_by_tex("BTC", YELLOW)
 | |
|         fees.set_color_by_tex("\\$", GREEN)
 | |
|         fees.to_edge(UP)
 | |
| 
 | |
|         self.play(Write(fees))
 | |
|         self.wait()
 | |
| 
 | |
| class HighlightingAFewFees(ExternallyAnimatedScene):
 | |
|     pass
 | |
| 
 | |
| class TopicsNotCovered(TeacherStudentsScene):
 | |
|     def construct(self):
 | |
|         title = TextMobject("Topics not covered:")
 | |
|         title.to_corner(UP+LEFT)
 | |
|         title.set_color(YELLOW)
 | |
|         title.save_state()
 | |
|         title.shift(DOWN)
 | |
|         title.set_fill(opacity = 0)
 | |
| 
 | |
|         topics = VGroup(*list(map(TextMobject, [
 | |
|             "Merkle trees",
 | |
|             "Alternatives to proof of work",
 | |
|             "Scripting",
 | |
|             "$\\vdots$",
 | |
|             "(See links in description)",
 | |
|         ])))
 | |
|         topics.arrange_submobjects(DOWN, aligned_edge = LEFT)
 | |
|         topics[-2].next_to(topics[-3], DOWN)
 | |
|         topics.next_to(title, RIGHT)
 | |
|         topics.to_edge(UP)
 | |
| 
 | |
|         self.play(
 | |
|             title.restore,
 | |
|             self.teacher.change_mode, "raise_right_hand"
 | |
|         )
 | |
|         for topic in topics:
 | |
|             self.change_student_modes(
 | |
|                 "confused", "thinking","pondering",
 | |
|                 look_at_arg = topic,
 | |
|                 added_anims = [LaggedStart(FadeIn, topic)]
 | |
|             )
 | |
|             self.wait()
 | |
| 
 | |
| class Exchange(Animation):
 | |
|     CONFIG = {
 | |
|         "rate_func" : None,
 | |
|     }
 | |
|     def __init__(self, exchange, **kwargs):
 | |
|         self.swap = Swap(
 | |
|             exchange.left,
 | |
|             exchange.right,
 | |
|         )
 | |
|         self.changed_symbols_yet = False
 | |
|         Animation.__init__(self, exchange, **kwargs)
 | |
| 
 | |
|     def update_mobject(self, alpha):
 | |
|         exchange = self.mobject
 | |
|         if alpha < 1./3:
 | |
|             self.swap.update(3*alpha)
 | |
|         elif alpha < 2./3:
 | |
|             sub_alpha = alpha*3 - 1
 | |
|             group = VGroup(exchange.left, exchange.right)
 | |
|             group.set_fill(opacity = 1-smooth(sub_alpha))
 | |
|         else:
 | |
|             if not self.changed_symbols_yet:
 | |
|                 new_left = random.choice(
 | |
|                     exchange.cryptocurrencies
 | |
|                 ).copy()
 | |
|                 new_left.move_to(exchange.right)
 | |
|                 new_right = random.choice(
 | |
|                     exchange.currencies
 | |
|                 ).copy()
 | |
|                 new_right.move_to(exchange.left)
 | |
|                 Transform(exchange.left, new_left).update(1)
 | |
|                 Transform(exchange.right, new_right).update(1)
 | |
|                 self.changed_symbols_yet = True
 | |
|             sub_alpha = 3*alpha - 2
 | |
|             group = VGroup(exchange.left, exchange.right)
 | |
|             group.set_fill(opacity = smooth(sub_alpha))
 | |
| 
 | |
| class ShowManyExchanges(Scene):
 | |
|     CONFIG = {
 | |
|         "n_rows" : 2,
 | |
|         "n_cols" : 8,
 | |
|         "shift_radius" : 0.5,
 | |
|         "run_time" : 30,
 | |
|     }
 | |
|     def construct(self):
 | |
|         cryptocurrencies = self.get_cryptocurrencies()
 | |
|         currencies = self.get_currencies()
 | |
|         for currency in it.chain(currencies, cryptocurrencies):
 | |
|             currency.set_height(0.5)
 | |
|             currency.align_data(EthereumLogo())
 | |
|         exchange = VGroup(*[
 | |
|             Arrow(
 | |
|                 p1, p2,
 | |
|                 path_arc = np.pi,
 | |
|                 buff = MED_LARGE_BUFF
 | |
|             )
 | |
|             for p1, p2 in (LEFT, RIGHT), (RIGHT, LEFT)
 | |
|         ]).set_color(WHITE)
 | |
|         exchanges = VGroup(*[
 | |
|             VGroup(*[
 | |
|                 exchange.copy()
 | |
|                 for x in range(3)
 | |
|             ]).arrange_submobjects(RIGHT, buff = 2*LARGE_BUFF)
 | |
|             for y in range(3)
 | |
|         ]).arrange_submobjects(DOWN, buff = MED_LARGE_BUFF)
 | |
|         exchanges = VGroup(*it.chain(*exchanges))
 | |
|         self.add(exchanges)
 | |
|         start_times = list(np.linspace(0, 2, len(exchanges)))
 | |
|         random.shuffle(start_times)
 | |
|         for exchange, start_time in zip(exchanges, start_times):
 | |
|             left = random.choice(cryptocurrencies).copy()
 | |
|             right = random.choice(currencies).copy()
 | |
|             left.move_to(exchange.get_left())
 | |
|             right.move_to(exchange.get_right())
 | |
| 
 | |
|             exchange.left = left
 | |
|             exchange.right = right
 | |
|             exchange.start_time = start_time
 | |
|             exchange.add(left, right)
 | |
|             exchange.currencies = currencies
 | |
|             exchange.cryptocurrencies = cryptocurrencies
 | |
|             exchange.animation = Exchange(exchange)
 | |
| 
 | |
|         times = np.arange(0, self.run_time, self.frame_duration)
 | |
|         from scene.scene import ProgressDisplay
 | |
|         for t in ProgressDisplay(times):
 | |
|             for exchange in exchanges:
 | |
|                 sub_t = t - exchange.start_time
 | |
|                 if sub_t < 0:
 | |
|                     continue
 | |
|                 elif sub_t > 3:
 | |
|                     exchange.start_time = t
 | |
|                     sub_t = 0
 | |
|                     exchange.animation = Exchange(exchange)
 | |
|                 exchange.animation.update(sub_t/3.0)
 | |
|             self.update_frame(
 | |
|                 self.extract_mobject_family_members(exchanges)
 | |
|             )
 | |
|             self.add_frames(self.get_frame())
 | |
| 
 | |
|     def get_cryptocurrencies(self):
 | |
|         return [
 | |
|             BitcoinLogo(),
 | |
|             BitcoinLogo(),
 | |
|             BitcoinLogo(),
 | |
|             EthereumLogo(),
 | |
|             LitecoinLogo()
 | |
|         ]
 | |
| 
 | |
|     def get_currencies(self):
 | |
|         return [
 | |
|             TexMobject("\\$").set_color(GREEN),
 | |
|             TexMobject("\\$").set_color(GREEN),
 | |
|             TexMobject("\\$").set_color(GREEN),
 | |
|             SVGMobject(
 | |
|                 file_name = "euro_symbol",
 | |
|                 stroke_width = 0,
 | |
|                 fill_opacity = 1,
 | |
|                 fill_color = BLUE,
 | |
|             ),
 | |
|             SVGMobject(
 | |
|                 file_name = "yen_symbol",
 | |
|                 stroke_width = 0,
 | |
|                 fill_opacity = 1,
 | |
|                 fill_color = RED,
 | |
|             )
 | |
|         ]
 | |
| 
 | |
| class ShowLDAndOtherCurrencyExchanges(ShowManyExchanges):
 | |
|     CONFIG = {
 | |
|         "run_time" : 15,
 | |
|     }
 | |
|     def get_cryptocurrencies(self):
 | |
|         euro = SVGMobject(
 | |
|             file_name = "euro_symbol",
 | |
|             stroke_width = 0,
 | |
|             fill_opacity = 1,
 | |
|             fill_color = BLUE,
 | |
|         )
 | |
|         return [
 | |
|             TextMobject("LD").set_color(YELLOW),
 | |
|             TextMobject("LD").set_color(YELLOW),
 | |
|             euro, euro.copy(),
 | |
|             SVGMobject(
 | |
|                 file_name = "yen_symbol",
 | |
|                 stroke_width = 0,
 | |
|                 fill_opacity = 1,
 | |
|                 fill_color = RED,
 | |
|             ),
 | |
|             BitcoinLogo(),
 | |
|         ]
 | |
| 
 | |
|     def get_currencies(self):
 | |
|         return [TexMobject("\\$").set_color(GREEN)]
 | |
| 
 | |
| class CryptoPatreonThanks(PatreonThanks):
 | |
|     CONFIG = {
 | |
|         "specific_patrons" : [
 | |
|             "Ali Yahya",
 | |
|             "Desmos",
 | |
|             "Burt Humburg",
 | |
|             "CrypticSwarm",
 | |
|             "Juan Benet",
 | |
|             "Samantha D. Suplee",
 | |
|             "James Park",
 | |
|             "Erik Sundell",
 | |
|             "Yana Chernobilsky",
 | |
|             "Kaustuv DeBiswas",
 | |
|             "Kathryn Schmiedicke",
 | |
|             "Karan Bhargava",
 | |
|             "Yu Jun",
 | |
|             "Dave Nicponski",
 | |
|             "Damion Kistler",
 | |
|             "Markus Persson",
 | |
|             "Yoni Nazarathy",
 | |
|             "Ed Kellett",
 | |
|             "Joseph John Cox",
 | |
|             "Dan Buchoff",
 | |
|             "Luc Ritchie",
 | |
|             "Andrew Busey",
 | |
|             "Michael McGuffin",
 | |
|             "John Haley",
 | |
|             "Mourits de Beer",
 | |
|             "Ankalagon",
 | |
|             "Eric Lavault",
 | |
|             "Tomohiro Furusawa",
 | |
|             "Boris Veselinovich",
 | |
|             "Julian Pulgarin",
 | |
|             "Jeff Linse",
 | |
|             "Cooper Jones",
 | |
|             "Ryan Dahl",
 | |
|             "Mark Govea",
 | |
|             "Robert Teed",
 | |
|             "Jason Hise",
 | |
|             "Meshal Alshammari",
 | |
|             "Bernd Sing",
 | |
|             "Nils Schneider",
 | |
|             "James Thornton",
 | |
|             "Mustafa Mahdi",
 | |
|             "Mathew Bramson",
 | |
|             "Jerry Ling",
 | |
|             "Vecht",
 | |
|             "Shimin Kuang",
 | |
|             "Rish Kundalia",
 | |
|             "Achille Brighton",
 | |
|             "Ripta Pasay",
 | |
|         ]
 | |
|     }
 | |
| 
 | |
| class ProtocolLabs(PiCreatureScene):
 | |
|     def construct(self):
 | |
|         morty = self.pi_creature
 | |
|         logo = self.get_logo()
 | |
|         logo.next_to(morty, UP)
 | |
|         logo.shift_onto_screen()
 | |
|         screen_rect = ScreenRectangle()
 | |
|         screen_rect.set_height(5)
 | |
|         screen_rect.to_edge(LEFT)
 | |
| 
 | |
|         self.play(
 | |
|             DrawBorderThenFill(logo[0]),
 | |
|             LaggedStart(FadeIn, logo[1]),
 | |
|             morty.change, "raise_right_hand",
 | |
|         )
 | |
|         self.wait()
 | |
|         self.play(
 | |
|             logo.scale, 0.5,
 | |
|             logo.to_corner, UP+LEFT,
 | |
|             ShowCreation(screen_rect)
 | |
|         )
 | |
|         modes = ["pondering", "happy", "happy"]
 | |
|         for mode in modes:
 | |
|             for x in range(2):
 | |
|                 self.play(Blink(morty))
 | |
|                 self.wait(3)
 | |
|             self.play(morty.change, mode, screen_rect)
 | |
| 
 | |
|     def get_logo(self):
 | |
|         logo = SVGMobject(
 | |
|             file_name = "protocol_labs_logo",
 | |
|             height = 1.5,
 | |
|             fill_color = WHITE,
 | |
|         )
 | |
|         name = SVGMobject(
 | |
|             file_name = "protocol_labs_name",
 | |
|             height = 0.5*logo.get_height(),
 | |
|             fill_color = LIGHT_GREY,
 | |
|         )
 | |
|         for mob in logo, name:
 | |
|             for submob in mob:
 | |
|                 submob.is_subpath = False
 | |
|         name.next_to(logo, RIGHT)
 | |
|         return VGroup(logo, name)
 | |
| 
 | |
| class Thumbnail(DistributedBlockChainScene):
 | |
|     CONFIG = {
 | |
|         "n_blocks" : 4,
 | |
|     }
 | |
|     def construct(self):
 | |
|         title = TextMobject("Crypto", "currencies", arg_separator = "")
 | |
|         title.scale(2.5)
 | |
|         title.to_edge(UP)
 | |
|         title[0].set_color(YELLOW)
 | |
|         title[0].set_stroke(RED, 2)
 | |
|         self.add(title)
 | |
| 
 | |
|         # logos = VGroup(
 | |
|         #     BitcoinLogo(), EthereumLogo(), LitecoinLogo()
 | |
|         # )
 | |
|         # for logo in logos:
 | |
|         #     logo.set_height(1)
 | |
|         # logos.add(TexMobject("\\dots").scale(2))
 | |
|         # logos.arrange_submobjects(RIGHT)
 | |
|         # logos.next_to(title, RIGHT, LARGE_BUFF)
 | |
|         # self.add(logos)
 | |
| 
 | |
|         block_chain = self.get_block_chain()
 | |
|         block_chain.arrows.set_color(RED)
 | |
|         block_chain.blocks.set_color_by_gradient(BLUE, GREEN)
 | |
|         block_chain.set_width(FRAME_WIDTH-1)
 | |
|         block_chain.set_stroke(width = 12)
 | |
|         self.add(block_chain)
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | 
